1*023aabb6SMauro Carvalho Chehab#!/usr/bin/env python3 2*023aabb6SMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0 3*023aabb6SMauro Carvalho Chehab# Copyright(c) 2025-2026: Mauro Carvalho Chehab <mchehab@kernel.org>. 4*023aabb6SMauro Carvalho Chehab# 5*023aabb6SMauro Carvalho Chehab# pylint: disable=C0103,R0912,R0914,E1101 6*023aabb6SMauro Carvalho Chehab 7*023aabb6SMauro Carvalho Chehab""" 8*023aabb6SMauro Carvalho ChehabProvides helper functions and classes execute python unit tests. 9*023aabb6SMauro Carvalho Chehab 10*023aabb6SMauro Carvalho ChehabThose help functions provide a nice colored output summary of each 11*023aabb6SMauro Carvalho Chehabexecuted test and, when a test fails, it shows the different in diff 12*023aabb6SMauro Carvalho Chehabformat when running in verbose mode, like:: 13*023aabb6SMauro Carvalho Chehab 14*023aabb6SMauro Carvalho Chehab $ tools/unittests/nested_match.py -v 15*023aabb6SMauro Carvalho Chehab ... 16*023aabb6SMauro Carvalho Chehab Traceback (most recent call last): 17*023aabb6SMauro Carvalho Chehab File "/new_devel/docs/tools/unittests/nested_match.py", line 69, in test_count_limit 18*023aabb6SMauro Carvalho Chehab self.assertEqual(replaced, "bar(a); bar(b); foo(c)") 19*023aabb6SMauro Carvalho Chehab ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 20*023aabb6SMauro Carvalho Chehab AssertionError: 'bar(a) foo(b); foo(c)' != 'bar(a); bar(b); foo(c)' 21*023aabb6SMauro Carvalho Chehab - bar(a) foo(b); foo(c) 22*023aabb6SMauro Carvalho Chehab ? ^^^^ 23*023aabb6SMauro Carvalho Chehab + bar(a); bar(b); foo(c) 24*023aabb6SMauro Carvalho Chehab ? ^^^^^ 25*023aabb6SMauro Carvalho Chehab ... 26*023aabb6SMauro Carvalho Chehab 27*023aabb6SMauro Carvalho ChehabIt also allows filtering what tests will be executed via ``-k`` parameter. 28*023aabb6SMauro Carvalho Chehab 29*023aabb6SMauro Carvalho ChehabTypical usage is to do:: 30*023aabb6SMauro Carvalho Chehab 31*023aabb6SMauro Carvalho Chehab from unittest_helper import run_unittest 32*023aabb6SMauro Carvalho Chehab ... 33*023aabb6SMauro Carvalho Chehab 34*023aabb6SMauro Carvalho Chehab if __name__ == "__main__": 35*023aabb6SMauro Carvalho Chehab run_unittest(__file__) 36*023aabb6SMauro Carvalho Chehab 37*023aabb6SMauro Carvalho ChehabIf passing arguments is needed, on a more complex scenario, it can be 38*023aabb6SMauro Carvalho Chehabused like on this example:: 39*023aabb6SMauro Carvalho Chehab 40*023aabb6SMauro Carvalho Chehab from unittest_helper import TestUnits, run_unittest 41*023aabb6SMauro Carvalho Chehab ... 42*023aabb6SMauro Carvalho Chehab env = {'sudo': ""} 43*023aabb6SMauro Carvalho Chehab ... 44*023aabb6SMauro Carvalho Chehab if __name__ == "__main__": 45*023aabb6SMauro Carvalho Chehab runner = TestUnits() 46*023aabb6SMauro Carvalho Chehab base_parser = runner.parse_args() 47*023aabb6SMauro Carvalho Chehab base_parser.add_argument('--sudo', action='store_true', 48*023aabb6SMauro Carvalho Chehab help='Enable tests requiring sudo privileges') 49*023aabb6SMauro Carvalho Chehab 50*023aabb6SMauro Carvalho Chehab args = base_parser.parse_args() 51*023aabb6SMauro Carvalho Chehab 52*023aabb6SMauro Carvalho Chehab # Update module-level flag 53*023aabb6SMauro Carvalho Chehab if args.sudo: 54*023aabb6SMauro Carvalho Chehab env['sudo'] = "1" 55*023aabb6SMauro Carvalho Chehab 56*023aabb6SMauro Carvalho Chehab # Run tests with customized arguments 57*023aabb6SMauro Carvalho Chehab runner.run(__file__, parser=base_parser, args=args, env=env) 58*023aabb6SMauro Carvalho Chehab""" 59*023aabb6SMauro Carvalho Chehab 60*023aabb6SMauro Carvalho Chehabimport argparse 61*023aabb6SMauro Carvalho Chehabimport atexit 62*023aabb6SMauro Carvalho Chehabimport os 63*023aabb6SMauro Carvalho Chehabimport re 64*023aabb6SMauro Carvalho Chehabimport unittest 65*023aabb6SMauro Carvalho Chehabimport sys 66*023aabb6SMauro Carvalho Chehab 67*023aabb6SMauro Carvalho Chehabfrom unittest.mock import patch 68*023aabb6SMauro Carvalho Chehab 69*023aabb6SMauro Carvalho Chehab 70*023aabb6SMauro Carvalho Chehabclass Summary(unittest.TestResult): 71*023aabb6SMauro Carvalho Chehab """ 72*023aabb6SMauro Carvalho Chehab Overrides ``unittest.TestResult`` class to provide a nice colored 73*023aabb6SMauro Carvalho Chehab summary. When in verbose mode, displays actual/expected difference in 74*023aabb6SMauro Carvalho Chehab unified diff format. 75*023aabb6SMauro Carvalho Chehab """ 76*023aabb6SMauro Carvalho Chehab def __init__(self, *args, **kwargs): 77*023aabb6SMauro Carvalho Chehab super().__init__(*args, **kwargs) 78*023aabb6SMauro Carvalho Chehab 79*023aabb6SMauro Carvalho Chehab #: Dictionary to store organized test results. 80*023aabb6SMauro Carvalho Chehab self.test_results = {} 81*023aabb6SMauro Carvalho Chehab 82*023aabb6SMauro Carvalho Chehab #: max length of the test names. 83*023aabb6SMauro Carvalho Chehab self.max_name_length = 0 84*023aabb6SMauro Carvalho Chehab 85*023aabb6SMauro Carvalho Chehab def startTest(self, test): 86*023aabb6SMauro Carvalho Chehab super().startTest(test) 87*023aabb6SMauro Carvalho Chehab test_id = test.id() 88*023aabb6SMauro Carvalho Chehab parts = test_id.split(".") 89*023aabb6SMauro Carvalho Chehab 90*023aabb6SMauro Carvalho Chehab # Extract module, class, and method names 91*023aabb6SMauro Carvalho Chehab if len(parts) >= 3: 92*023aabb6SMauro Carvalho Chehab module_name = parts[-3] 93*023aabb6SMauro Carvalho Chehab else: 94*023aabb6SMauro Carvalho Chehab module_name = "" 95*023aabb6SMauro Carvalho Chehab if len(parts) >= 2: 96*023aabb6SMauro Carvalho Chehab class_name = parts[-2] 97*023aabb6SMauro Carvalho Chehab else: 98*023aabb6SMauro Carvalho Chehab class_name = "" 99*023aabb6SMauro Carvalho Chehab 100*023aabb6SMauro Carvalho Chehab method_name = parts[-1] 101*023aabb6SMauro Carvalho Chehab 102*023aabb6SMauro Carvalho Chehab # Build the hierarchical structure 103*023aabb6SMauro Carvalho Chehab if module_name not in self.test_results: 104*023aabb6SMauro Carvalho Chehab self.test_results[module_name] = {} 105*023aabb6SMauro Carvalho Chehab 106*023aabb6SMauro Carvalho Chehab if class_name not in self.test_results[module_name]: 107*023aabb6SMauro Carvalho Chehab self.test_results[module_name][class_name] = [] 108*023aabb6SMauro Carvalho Chehab 109*023aabb6SMauro Carvalho Chehab # Track maximum test name length for alignment 110*023aabb6SMauro Carvalho Chehab display_name = f"{method_name}:" 111*023aabb6SMauro Carvalho Chehab 112*023aabb6SMauro Carvalho Chehab self.max_name_length = max(len(display_name), self.max_name_length) 113*023aabb6SMauro Carvalho Chehab 114*023aabb6SMauro Carvalho Chehab def _record_test(self, test, status): 115*023aabb6SMauro Carvalho Chehab test_id = test.id() 116*023aabb6SMauro Carvalho Chehab parts = test_id.split(".") 117*023aabb6SMauro Carvalho Chehab if len(parts) >= 3: 118*023aabb6SMauro Carvalho Chehab module_name = parts[-3] 119*023aabb6SMauro Carvalho Chehab else: 120*023aabb6SMauro Carvalho Chehab module_name = "" 121*023aabb6SMauro Carvalho Chehab if len(parts) >= 2: 122*023aabb6SMauro Carvalho Chehab class_name = parts[-2] 123*023aabb6SMauro Carvalho Chehab else: 124*023aabb6SMauro Carvalho Chehab class_name = "" 125*023aabb6SMauro Carvalho Chehab method_name = parts[-1] 126*023aabb6SMauro Carvalho Chehab self.test_results[module_name][class_name].append((method_name, status)) 127*023aabb6SMauro Carvalho Chehab 128*023aabb6SMauro Carvalho Chehab def addSuccess(self, test): 129*023aabb6SMauro Carvalho Chehab super().addSuccess(test) 130*023aabb6SMauro Carvalho Chehab self._record_test(test, "OK") 131*023aabb6SMauro Carvalho Chehab 132*023aabb6SMauro Carvalho Chehab def addFailure(self, test, err): 133*023aabb6SMauro Carvalho Chehab super().addFailure(test, err) 134*023aabb6SMauro Carvalho Chehab self._record_test(test, "FAIL") 135*023aabb6SMauro Carvalho Chehab 136*023aabb6SMauro Carvalho Chehab def addError(self, test, err): 137*023aabb6SMauro Carvalho Chehab super().addError(test, err) 138*023aabb6SMauro Carvalho Chehab self._record_test(test, "ERROR") 139*023aabb6SMauro Carvalho Chehab 140*023aabb6SMauro Carvalho Chehab def addSkip(self, test, reason): 141*023aabb6SMauro Carvalho Chehab super().addSkip(test, reason) 142*023aabb6SMauro Carvalho Chehab self._record_test(test, f"SKIP ({reason})") 143*023aabb6SMauro Carvalho Chehab 144*023aabb6SMauro Carvalho Chehab def printResults(self): 145*023aabb6SMauro Carvalho Chehab """ 146*023aabb6SMauro Carvalho Chehab Print results using colors if tty. 147*023aabb6SMauro Carvalho Chehab """ 148*023aabb6SMauro Carvalho Chehab # Check for ANSI color support 149*023aabb6SMauro Carvalho Chehab use_color = sys.stdout.isatty() 150*023aabb6SMauro Carvalho Chehab COLORS = { 151*023aabb6SMauro Carvalho Chehab "OK": "\033[32m", # Green 152*023aabb6SMauro Carvalho Chehab "FAIL": "\033[31m", # Red 153*023aabb6SMauro Carvalho Chehab "SKIP": "\033[1;33m", # Yellow 154*023aabb6SMauro Carvalho Chehab "PARTIAL": "\033[33m", # Orange 155*023aabb6SMauro Carvalho Chehab "EXPECTED_FAIL": "\033[36m", # Cyan 156*023aabb6SMauro Carvalho Chehab "reset": "\033[0m", # Reset to default terminal color 157*023aabb6SMauro Carvalho Chehab } 158*023aabb6SMauro Carvalho Chehab if not use_color: 159*023aabb6SMauro Carvalho Chehab for c in COLORS: 160*023aabb6SMauro Carvalho Chehab COLORS[c] = "" 161*023aabb6SMauro Carvalho Chehab 162*023aabb6SMauro Carvalho Chehab # Calculate maximum test name length 163*023aabb6SMauro Carvalho Chehab if not self.test_results: 164*023aabb6SMauro Carvalho Chehab return 165*023aabb6SMauro Carvalho Chehab try: 166*023aabb6SMauro Carvalho Chehab lengths = [] 167*023aabb6SMauro Carvalho Chehab for module in self.test_results.values(): 168*023aabb6SMauro Carvalho Chehab for tests in module.values(): 169*023aabb6SMauro Carvalho Chehab for test_name, _ in tests: 170*023aabb6SMauro Carvalho Chehab lengths.append(len(test_name) + 1) # +1 for colon 171*023aabb6SMauro Carvalho Chehab max_length = max(lengths) + 2 # Additional padding 172*023aabb6SMauro Carvalho Chehab except ValueError: 173*023aabb6SMauro Carvalho Chehab sys.exit("Test list is empty") 174*023aabb6SMauro Carvalho Chehab 175*023aabb6SMauro Carvalho Chehab # Print results 176*023aabb6SMauro Carvalho Chehab for module_name, classes in self.test_results.items(): 177*023aabb6SMauro Carvalho Chehab print(f"{module_name}:") 178*023aabb6SMauro Carvalho Chehab for class_name, tests in classes.items(): 179*023aabb6SMauro Carvalho Chehab print(f" {class_name}:") 180*023aabb6SMauro Carvalho Chehab for test_name, status in tests: 181*023aabb6SMauro Carvalho Chehab # Get base status without reason for SKIP 182*023aabb6SMauro Carvalho Chehab if status.startswith("SKIP"): 183*023aabb6SMauro Carvalho Chehab status_code = status.split()[0] 184*023aabb6SMauro Carvalho Chehab else: 185*023aabb6SMauro Carvalho Chehab status_code = status 186*023aabb6SMauro Carvalho Chehab color = COLORS.get(status_code, "") 187*023aabb6SMauro Carvalho Chehab print( 188*023aabb6SMauro Carvalho Chehab f" {test_name + ':':<{max_length}}{color}{status}{COLORS['reset']}" 189*023aabb6SMauro Carvalho Chehab ) 190*023aabb6SMauro Carvalho Chehab print() 191*023aabb6SMauro Carvalho Chehab 192*023aabb6SMauro Carvalho Chehab # Print summary 193*023aabb6SMauro Carvalho Chehab print(f"\nRan {self.testsRun} tests", end="") 194*023aabb6SMauro Carvalho Chehab if hasattr(self, "timeTaken"): 195*023aabb6SMauro Carvalho Chehab print(f" in {self.timeTaken:.3f}s", end="") 196*023aabb6SMauro Carvalho Chehab print() 197*023aabb6SMauro Carvalho Chehab 198*023aabb6SMauro Carvalho Chehab if not self.wasSuccessful(): 199*023aabb6SMauro Carvalho Chehab print(f"\n{COLORS['FAIL']}FAILED (", end="") 200*023aabb6SMauro Carvalho Chehab failures = getattr(self, "failures", []) 201*023aabb6SMauro Carvalho Chehab errors = getattr(self, "errors", []) 202*023aabb6SMauro Carvalho Chehab if failures: 203*023aabb6SMauro Carvalho Chehab print(f"failures={len(failures)}", end="") 204*023aabb6SMauro Carvalho Chehab if errors: 205*023aabb6SMauro Carvalho Chehab if failures: 206*023aabb6SMauro Carvalho Chehab print(", ", end="") 207*023aabb6SMauro Carvalho Chehab print(f"errors={len(errors)}", end="") 208*023aabb6SMauro Carvalho Chehab print(f"){COLORS['reset']}") 209*023aabb6SMauro Carvalho Chehab 210*023aabb6SMauro Carvalho Chehab 211*023aabb6SMauro Carvalho Chehabdef flatten_suite(suite): 212*023aabb6SMauro Carvalho Chehab """Flatten test suite hierarchy.""" 213*023aabb6SMauro Carvalho Chehab tests = [] 214*023aabb6SMauro Carvalho Chehab for item in suite: 215*023aabb6SMauro Carvalho Chehab if isinstance(item, unittest.TestSuite): 216*023aabb6SMauro Carvalho Chehab tests.extend(flatten_suite(item)) 217*023aabb6SMauro Carvalho Chehab else: 218*023aabb6SMauro Carvalho Chehab tests.append(item) 219*023aabb6SMauro Carvalho Chehab return tests 220*023aabb6SMauro Carvalho Chehab 221*023aabb6SMauro Carvalho Chehab 222*023aabb6SMauro Carvalho Chehabclass TestUnits: 223*023aabb6SMauro Carvalho Chehab """ 224*023aabb6SMauro Carvalho Chehab Helper class to set verbosity level. 225*023aabb6SMauro Carvalho Chehab 226*023aabb6SMauro Carvalho Chehab This class discover test files, import its unittest classes and 227*023aabb6SMauro Carvalho Chehab executes the test on it. 228*023aabb6SMauro Carvalho Chehab """ 229*023aabb6SMauro Carvalho Chehab def parse_args(self): 230*023aabb6SMauro Carvalho Chehab """Returns a parser for command line arguments.""" 231*023aabb6SMauro Carvalho Chehab parser = argparse.ArgumentParser(description="Test runner with regex filtering") 232*023aabb6SMauro Carvalho Chehab parser.add_argument("-v", "--verbose", action="count", default=1) 233*023aabb6SMauro Carvalho Chehab parser.add_argument("-f", "--failfast", action="store_true") 234*023aabb6SMauro Carvalho Chehab parser.add_argument("-k", "--keyword", 235*023aabb6SMauro Carvalho Chehab help="Regex pattern to filter test methods") 236*023aabb6SMauro Carvalho Chehab return parser 237*023aabb6SMauro Carvalho Chehab 238*023aabb6SMauro Carvalho Chehab def run(self, caller_file=None, pattern=None, 239*023aabb6SMauro Carvalho Chehab suite=None, parser=None, args=None, env=None): 240*023aabb6SMauro Carvalho Chehab """ 241*023aabb6SMauro Carvalho Chehab Execute all tests from the unity test file. 242*023aabb6SMauro Carvalho Chehab 243*023aabb6SMauro Carvalho Chehab It contains several optional parameters: 244*023aabb6SMauro Carvalho Chehab 245*023aabb6SMauro Carvalho Chehab ``caller_file``: 246*023aabb6SMauro Carvalho Chehab - name of the file that contains test. 247*023aabb6SMauro Carvalho Chehab 248*023aabb6SMauro Carvalho Chehab typical usage is to place __file__ at the caller test, e.g.:: 249*023aabb6SMauro Carvalho Chehab 250*023aabb6SMauro Carvalho Chehab if __name__ == "__main__": 251*023aabb6SMauro Carvalho Chehab TestUnits().run(__file__) 252*023aabb6SMauro Carvalho Chehab 253*023aabb6SMauro Carvalho Chehab ``pattern``: 254*023aabb6SMauro Carvalho Chehab - optional pattern to match multiple file names. Defaults 255*023aabb6SMauro Carvalho Chehab to basename of ``caller_file``. 256*023aabb6SMauro Carvalho Chehab 257*023aabb6SMauro Carvalho Chehab ``suite``: 258*023aabb6SMauro Carvalho Chehab - an unittest suite initialized by the caller using 259*023aabb6SMauro Carvalho Chehab ``unittest.TestLoader().discover()``. 260*023aabb6SMauro Carvalho Chehab 261*023aabb6SMauro Carvalho Chehab ``parser``: 262*023aabb6SMauro Carvalho Chehab - an argparse parser. If not defined, this helper will create 263*023aabb6SMauro Carvalho Chehab one. 264*023aabb6SMauro Carvalho Chehab 265*023aabb6SMauro Carvalho Chehab ``args``: 266*023aabb6SMauro Carvalho Chehab - an ``argparse.Namespace`` data filled by the caller. 267*023aabb6SMauro Carvalho Chehab 268*023aabb6SMauro Carvalho Chehab ``env``: 269*023aabb6SMauro Carvalho Chehab - environment variables that will be passed to the test suite 270*023aabb6SMauro Carvalho Chehab 271*023aabb6SMauro Carvalho Chehab At least ``caller_file`` or ``suite`` must be used, otherwise a 272*023aabb6SMauro Carvalho Chehab ``TypeError`` will be raised. 273*023aabb6SMauro Carvalho Chehab """ 274*023aabb6SMauro Carvalho Chehab if not args: 275*023aabb6SMauro Carvalho Chehab if not parser: 276*023aabb6SMauro Carvalho Chehab parser = self.parse_args() 277*023aabb6SMauro Carvalho Chehab args = parser.parse_args() 278*023aabb6SMauro Carvalho Chehab 279*023aabb6SMauro Carvalho Chehab if not caller_file and not suite: 280*023aabb6SMauro Carvalho Chehab raise TypeError("Either caller_file or suite is needed at TestUnits") 281*023aabb6SMauro Carvalho Chehab 282*023aabb6SMauro Carvalho Chehab verbose = args.verbose 283*023aabb6SMauro Carvalho Chehab 284*023aabb6SMauro Carvalho Chehab if not env: 285*023aabb6SMauro Carvalho Chehab env = os.environ.copy() 286*023aabb6SMauro Carvalho Chehab 287*023aabb6SMauro Carvalho Chehab env["VERBOSE"] = f"{verbose}" 288*023aabb6SMauro Carvalho Chehab 289*023aabb6SMauro Carvalho Chehab patcher = patch.dict(os.environ, env) 290*023aabb6SMauro Carvalho Chehab patcher.start() 291*023aabb6SMauro Carvalho Chehab # ensure it gets stopped after 292*023aabb6SMauro Carvalho Chehab atexit.register(patcher.stop) 293*023aabb6SMauro Carvalho Chehab 294*023aabb6SMauro Carvalho Chehab 295*023aabb6SMauro Carvalho Chehab if verbose >= 2: 296*023aabb6SMauro Carvalho Chehab unittest.TextTestRunner(verbosity=verbose).run = lambda suite: suite 297*023aabb6SMauro Carvalho Chehab 298*023aabb6SMauro Carvalho Chehab # Load ONLY tests from the calling file 299*023aabb6SMauro Carvalho Chehab if not suite: 300*023aabb6SMauro Carvalho Chehab if not pattern: 301*023aabb6SMauro Carvalho Chehab pattern = caller_file 302*023aabb6SMauro Carvalho Chehab 303*023aabb6SMauro Carvalho Chehab loader = unittest.TestLoader() 304*023aabb6SMauro Carvalho Chehab suite = loader.discover(start_dir=os.path.dirname(caller_file), 305*023aabb6SMauro Carvalho Chehab pattern=os.path.basename(caller_file)) 306*023aabb6SMauro Carvalho Chehab 307*023aabb6SMauro Carvalho Chehab # Flatten the suite for environment injection 308*023aabb6SMauro Carvalho Chehab tests_to_inject = flatten_suite(suite) 309*023aabb6SMauro Carvalho Chehab 310*023aabb6SMauro Carvalho Chehab # Filter tests by method name if -k specified 311*023aabb6SMauro Carvalho Chehab if args.keyword: 312*023aabb6SMauro Carvalho Chehab try: 313*023aabb6SMauro Carvalho Chehab pattern = re.compile(args.keyword) 314*023aabb6SMauro Carvalho Chehab filtered_suite = unittest.TestSuite() 315*023aabb6SMauro Carvalho Chehab for test in tests_to_inject: # Use the pre-flattened list 316*023aabb6SMauro Carvalho Chehab method_name = test.id().split(".")[-1] 317*023aabb6SMauro Carvalho Chehab if pattern.search(method_name): 318*023aabb6SMauro Carvalho Chehab filtered_suite.addTest(test) 319*023aabb6SMauro Carvalho Chehab suite = filtered_suite 320*023aabb6SMauro Carvalho Chehab except re.error as e: 321*023aabb6SMauro Carvalho Chehab sys.stderr.write(f"Invalid regex pattern: {e}\n") 322*023aabb6SMauro Carvalho Chehab sys.exit(1) 323*023aabb6SMauro Carvalho Chehab else: 324*023aabb6SMauro Carvalho Chehab # Maintain original suite structure if no keyword filtering 325*023aabb6SMauro Carvalho Chehab suite = unittest.TestSuite(tests_to_inject) 326*023aabb6SMauro Carvalho Chehab 327*023aabb6SMauro Carvalho Chehab if verbose >= 2: 328*023aabb6SMauro Carvalho Chehab resultclass = None 329*023aabb6SMauro Carvalho Chehab else: 330*023aabb6SMauro Carvalho Chehab resultclass = Summary 331*023aabb6SMauro Carvalho Chehab 332*023aabb6SMauro Carvalho Chehab runner = unittest.TextTestRunner(verbosity=args.verbose, 333*023aabb6SMauro Carvalho Chehab resultclass=resultclass, 334*023aabb6SMauro Carvalho Chehab failfast=args.failfast) 335*023aabb6SMauro Carvalho Chehab result = runner.run(suite) 336*023aabb6SMauro Carvalho Chehab if resultclass: 337*023aabb6SMauro Carvalho Chehab result.printResults() 338*023aabb6SMauro Carvalho Chehab 339*023aabb6SMauro Carvalho Chehab sys.exit(not result.wasSuccessful()) 340*023aabb6SMauro Carvalho Chehab 341*023aabb6SMauro Carvalho Chehab 342*023aabb6SMauro Carvalho Chehabdef run_unittest(fname): 343*023aabb6SMauro Carvalho Chehab """ 344*023aabb6SMauro Carvalho Chehab Basic usage of TestUnits class. 345*023aabb6SMauro Carvalho Chehab 346*023aabb6SMauro Carvalho Chehab Use it when there's no need to pass any extra argument to the tests 347*023aabb6SMauro Carvalho Chehab with. The recommended way is to place this at the end of each 348*023aabb6SMauro Carvalho Chehab unittest module:: 349*023aabb6SMauro Carvalho Chehab 350*023aabb6SMauro Carvalho Chehab if __name__ == "__main__": 351*023aabb6SMauro Carvalho Chehab run_unittest(__file__) 352*023aabb6SMauro Carvalho Chehab """ 353*023aabb6SMauro Carvalho Chehab TestUnits().run(fname) 354