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