b4f9b7f498fa23e66443ffcd5e5834273d1fb579
[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         # Trying adding a lot of data at once
421         self.node.AddData('data', tools.GetBytes(65, 20000))
422         self.dtb.Sync(auto_resize=True)
423
424     def testFromData(self):
425         dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
426         self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
427
428         self.node.AddEmptyProp('empty', 5)
429         self.dtb.Sync(auto_resize=True)
430         self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
431
432     def testMissingSetInt(self):
433         """Test handling of a missing property with SetInt"""
434         with self.assertRaises(ValueError) as e:
435             self.node.SetInt('one', 1)
436         self.assertIn("node '/spl-test': Missing property 'one'",
437                       str(e.exception))
438
439     def testMissingSetData(self):
440         """Test handling of a missing property with SetData"""
441         with self.assertRaises(ValueError) as e:
442             self.node.SetData('one', b'data')
443         self.assertIn("node '/spl-test': Missing property 'one'",
444                       str(e.exception))
445
446     def testMissingSetString(self):
447         """Test handling of a missing property with SetString"""
448         with self.assertRaises(ValueError) as e:
449             self.node.SetString('one', 1)
450         self.assertIn("node '/spl-test': Missing property 'one'",
451                       str(e.exception))
452
453     def testGetFilename(self):
454         """Test the dtb filename can be provided"""
455         self.assertEqual(tools.GetOutputFilename('source.dtb'),
456                          self.dtb.GetFilename())
457
458
459 class TestFdtUtil(unittest.TestCase):
460     """Tests for the fdt_util module
461
462     This module will likely be mostly replaced at some point, once upstream
463     libfdt has better Python support. For now, this provides tests for current
464     functionality.
465     """
466     @classmethod
467     def setUpClass(cls):
468         tools.PrepareOutputDir(None)
469
470     @classmethod
471     def tearDownClass(cls):
472         tools.FinaliseOutputDir()
473
474     def setUp(self):
475         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
476         self.node = self.dtb.GetNode('/spl-test')
477
478     def testGetInt(self):
479         self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
480         self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
481
482         with self.assertRaises(ValueError) as e:
483             self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
484         self.assertIn("property 'intarray' has list value: expecting a single "
485                       'integer', str(e.exception))
486
487     def testGetString(self):
488         self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
489         self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
490                                                     'test'))
491
492         with self.assertRaises(ValueError) as e:
493             self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
494         self.assertIn("property 'stringarray' has list value: expecting a "
495                       'single string', str(e.exception))
496
497     def testGetBool(self):
498         self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
499         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
500         self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
501         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
502
503     def testGetByte(self):
504         self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
505         self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
506
507         with self.assertRaises(ValueError) as e:
508             fdt_util.GetByte(self.node, 'longbytearray')
509         self.assertIn("property 'longbytearray' has list value: expecting a "
510                       'single byte', str(e.exception))
511
512         with self.assertRaises(ValueError) as e:
513             fdt_util.GetByte(self.node, 'intval')
514         self.assertIn("property 'intval' has length 4, expecting 1",
515                       str(e.exception))
516
517     def testGetPhandleList(self):
518         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
519         node = dtb.GetNode('/phandle-source2')
520         self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
521         node = dtb.GetNode('/phandle-source')
522         self.assertEqual([1, 2, 11, 3, 12, 13, 1],
523                          fdt_util.GetPhandleList(node, 'clocks'))
524         self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
525
526     def testGetDataType(self):
527         self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
528         self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
529                                                          str))
530         with self.assertRaises(ValueError) as e:
531             self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
532                                                      bool))
533     def testFdtCellsToCpu(self):
534         val = self.node.props['intarray'].value
535         self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
536         self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
537
538         dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
539         node1 = dtb2.GetNode('/test1')
540         val = node1.props['reg'].value
541         self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
542
543         node2 = dtb2.GetNode('/test2')
544         val = node2.props['reg'].value
545         self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
546         self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
547                                                                        2))
548         self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
549
550     def testEnsureCompiled(self):
551         """Test a degenerate case of this function (file already compiled)"""
552         dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
553         self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
554
555     def testEnsureCompiledTmpdir(self):
556         """Test providing a temporary directory"""
557         try:
558             old_outdir = tools.outdir
559             tools.outdir= None
560             tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
561             dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts',
562                                           tmpdir)
563             self.assertEqual(tmpdir, os.path.dirname(dtb))
564             shutil.rmtree(tmpdir)
565         finally:
566             tools.outdir= old_outdir
567
568
569 def RunTestCoverage():
570     """Run the tests and check that we get 100% coverage"""
571     test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
572             ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
573
574
575 def RunTests(args):
576     """Run all the test we have for the fdt model
577
578     Args:
579         args: List of positional args provided to fdt. This can hold a test
580             name to execute (as in 'fdt -t testFdt', for example)
581     """
582     result = unittest.TestResult()
583     sys.argv = [sys.argv[0]]
584     test_name = args and args[0] or None
585     for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
586         if test_name:
587             try:
588                 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
589             except AttributeError:
590                 continue
591         else:
592             suite = unittest.TestLoader().loadTestsFromTestCase(module)
593         suite.run(result)
594
595     print(result)
596     for _, err in result.errors:
597         print(err)
598     for _, err in result.failures:
599         print(err)
600
601 if __name__ != '__main__':
602     sys.exit(1)
603
604 parser = OptionParser()
605 parser.add_option('-B', '--build-dir', type='string', default='b',
606         help='Directory containing the build output')
607 parser.add_option('-P', '--processes', type=int,
608                   help='set number of processes to use for running tests')
609 parser.add_option('-t', '--test', action='store_true', dest='test',
610                   default=False, help='run tests')
611 parser.add_option('-T', '--test-coverage', action='store_true',
612                 default=False, help='run tests and check for 100% coverage')
613 (options, args) = parser.parse_args()
614
615 # Run our meagre tests
616 if options.test:
617     RunTests(args)
618 elif options.test_coverage:
619     RunTestCoverage()