1023aabb6SMauro Carvalho Chehab#!/usr/bin/env python3 2023aabb6SMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0 3023aabb6SMauro Carvalho Chehab# Copyright(c) 2025-2026: Mauro Carvalho Chehab <mchehab@kernel.org>. 4023aabb6SMauro Carvalho Chehab# 5023aabb6SMauro Carvalho Chehab# pylint: disable=C0103,R0912,R0914,E1101 6023aabb6SMauro Carvalho Chehab 7023aabb6SMauro Carvalho Chehab""" 8023aabb6SMauro Carvalho ChehabProvides helper functions and classes execute python unit tests. 9023aabb6SMauro Carvalho Chehab 10023aabb6SMauro Carvalho ChehabThose help functions provide a nice colored output summary of each 11023aabb6SMauro Carvalho Chehabexecuted test and, when a test fails, it shows the different in diff 12023aabb6SMauro Carvalho Chehabformat when running in verbose mode, like:: 13023aabb6SMauro Carvalho Chehab 14023aabb6SMauro Carvalho Chehab $ tools/unittests/nested_match.py -v 15023aabb6SMauro Carvalho Chehab ... 16023aabb6SMauro Carvalho Chehab Traceback (most recent call last): 17023aabb6SMauro Carvalho Chehab File "/new_devel/docs/tools/unittests/nested_match.py", line 69, in test_count_limit 18023aabb6SMauro Carvalho Chehab self.assertEqual(replaced, "bar(a); bar(b); foo(c)") 19023aabb6SMauro Carvalho Chehab ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 20023aabb6SMauro Carvalho Chehab AssertionError: 'bar(a) foo(b); foo(c)' != 'bar(a); bar(b); foo(c)' 21023aabb6SMauro Carvalho Chehab - bar(a) foo(b); foo(c) 22023aabb6SMauro Carvalho Chehab ? ^^^^ 23023aabb6SMauro Carvalho Chehab + bar(a); bar(b); foo(c) 24023aabb6SMauro Carvalho Chehab ? ^^^^^ 25023aabb6SMauro Carvalho Chehab ... 26023aabb6SMauro Carvalho Chehab 27023aabb6SMauro Carvalho ChehabIt also allows filtering what tests will be executed via ``-k`` parameter. 28023aabb6SMauro Carvalho Chehab 29023aabb6SMauro Carvalho ChehabTypical usage is to do:: 30023aabb6SMauro Carvalho Chehab 31023aabb6SMauro Carvalho Chehab from unittest_helper import run_unittest 32023aabb6SMauro Carvalho Chehab ... 33023aabb6SMauro Carvalho Chehab 34023aabb6SMauro Carvalho Chehab if __name__ == "__main__": 35023aabb6SMauro Carvalho Chehab run_unittest(__file__) 36023aabb6SMauro Carvalho Chehab 37023aabb6SMauro Carvalho ChehabIf passing arguments is needed, on a more complex scenario, it can be 38023aabb6SMauro Carvalho Chehabused like on this example:: 39023aabb6SMauro Carvalho Chehab 40023aabb6SMauro Carvalho Chehab from unittest_helper import TestUnits, run_unittest 41023aabb6SMauro Carvalho Chehab ... 42023aabb6SMauro Carvalho Chehab env = {'sudo': ""} 43023aabb6SMauro Carvalho Chehab ... 44023aabb6SMauro Carvalho Chehab if __name__ == "__main__": 45023aabb6SMauro Carvalho Chehab runner = TestUnits() 46023aabb6SMauro Carvalho Chehab base_parser = runner.parse_args() 47023aabb6SMauro Carvalho Chehab base_parser.add_argument('--sudo', action='store_true', 48023aabb6SMauro Carvalho Chehab help='Enable tests requiring sudo privileges') 49023aabb6SMauro Carvalho Chehab 50023aabb6SMauro Carvalho Chehab args = base_parser.parse_args() 51023aabb6SMauro Carvalho Chehab 52023aabb6SMauro Carvalho Chehab # Update module-level flag 53023aabb6SMauro Carvalho Chehab if args.sudo: 54023aabb6SMauro Carvalho Chehab env['sudo'] = "1" 55023aabb6SMauro Carvalho Chehab 56023aabb6SMauro Carvalho Chehab # Run tests with customized arguments 57023aabb6SMauro Carvalho Chehab runner.run(__file__, parser=base_parser, args=args, env=env) 58023aabb6SMauro Carvalho Chehab""" 59023aabb6SMauro Carvalho Chehab 60023aabb6SMauro Carvalho Chehabimport argparse 61023aabb6SMauro Carvalho Chehabimport atexit 62023aabb6SMauro Carvalho Chehabimport os 63023aabb6SMauro Carvalho Chehabimport re 64023aabb6SMauro Carvalho Chehabimport unittest 65023aabb6SMauro Carvalho Chehabimport sys 66023aabb6SMauro Carvalho Chehab 67023aabb6SMauro Carvalho Chehabfrom unittest.mock import patch 68023aabb6SMauro Carvalho Chehab 69023aabb6SMauro Carvalho Chehab 70023aabb6SMauro Carvalho Chehabclass Summary(unittest.TestResult): 71023aabb6SMauro Carvalho Chehab """ 72023aabb6SMauro Carvalho Chehab Overrides ``unittest.TestResult`` class to provide a nice colored 73023aabb6SMauro Carvalho Chehab summary. When in verbose mode, displays actual/expected difference in 74023aabb6SMauro Carvalho Chehab unified diff format. 75023aabb6SMauro Carvalho Chehab """ 76023aabb6SMauro Carvalho Chehab def __init__(self, *args, **kwargs): 77023aabb6SMauro Carvalho Chehab super().__init__(*args, **kwargs) 78023aabb6SMauro Carvalho Chehab 79023aabb6SMauro Carvalho Chehab #: Dictionary to store organized test results. 80023aabb6SMauro Carvalho Chehab self.test_results = {} 81023aabb6SMauro Carvalho Chehab 82023aabb6SMauro Carvalho Chehab #: max length of the test names. 83023aabb6SMauro Carvalho Chehab self.max_name_length = 0 84023aabb6SMauro Carvalho Chehab 85023aabb6SMauro Carvalho Chehab def startTest(self, test): 86023aabb6SMauro Carvalho Chehab super().startTest(test) 87023aabb6SMauro Carvalho Chehab test_id = test.id() 88023aabb6SMauro Carvalho Chehab parts = test_id.split(".") 89023aabb6SMauro Carvalho Chehab 90023aabb6SMauro Carvalho Chehab # Extract module, class, and method names 91023aabb6SMauro Carvalho Chehab if len(parts) >= 3: 92023aabb6SMauro Carvalho Chehab module_name = parts[-3] 93023aabb6SMauro Carvalho Chehab else: 94023aabb6SMauro Carvalho Chehab module_name = "" 95023aabb6SMauro Carvalho Chehab if len(parts) >= 2: 96023aabb6SMauro Carvalho Chehab class_name = parts[-2] 97023aabb6SMauro Carvalho Chehab else: 98023aabb6SMauro Carvalho Chehab class_name = "" 99023aabb6SMauro Carvalho Chehab 100023aabb6SMauro Carvalho Chehab method_name = parts[-1] 101023aabb6SMauro Carvalho Chehab 102023aabb6SMauro Carvalho Chehab # Build the hierarchical structure 103023aabb6SMauro Carvalho Chehab if module_name not in self.test_results: 104023aabb6SMauro Carvalho Chehab self.test_results[module_name] = {} 105023aabb6SMauro Carvalho Chehab 106023aabb6SMauro Carvalho Chehab if class_name not in self.test_results[module_name]: 107023aabb6SMauro Carvalho Chehab self.test_results[module_name][class_name] = [] 108023aabb6SMauro Carvalho Chehab 109023aabb6SMauro Carvalho Chehab # Track maximum test name length for alignment 110023aabb6SMauro Carvalho Chehab display_name = f"{method_name}:" 111023aabb6SMauro Carvalho Chehab 112023aabb6SMauro Carvalho Chehab self.max_name_length = max(len(display_name), self.max_name_length) 113023aabb6SMauro Carvalho Chehab 114023aabb6SMauro Carvalho Chehab def _record_test(self, test, status): 115023aabb6SMauro Carvalho Chehab test_id = test.id() 116023aabb6SMauro Carvalho Chehab parts = test_id.split(".") 117023aabb6SMauro Carvalho Chehab if len(parts) >= 3: 118023aabb6SMauro Carvalho Chehab module_name = parts[-3] 119023aabb6SMauro Carvalho Chehab else: 120023aabb6SMauro Carvalho Chehab module_name = "" 121023aabb6SMauro Carvalho Chehab if len(parts) >= 2: 122023aabb6SMauro Carvalho Chehab class_name = parts[-2] 123023aabb6SMauro Carvalho Chehab else: 124023aabb6SMauro Carvalho Chehab class_name = "" 125023aabb6SMauro Carvalho Chehab method_name = parts[-1] 126023aabb6SMauro Carvalho Chehab self.test_results[module_name][class_name].append((method_name, status)) 127023aabb6SMauro Carvalho Chehab 128023aabb6SMauro Carvalho Chehab def addSuccess(self, test): 129023aabb6SMauro Carvalho Chehab super().addSuccess(test) 130023aabb6SMauro Carvalho Chehab self._record_test(test, "OK") 131023aabb6SMauro Carvalho Chehab 132023aabb6SMauro Carvalho Chehab def addFailure(self, test, err): 133023aabb6SMauro Carvalho Chehab super().addFailure(test, err) 134023aabb6SMauro Carvalho Chehab self._record_test(test, "FAIL") 135023aabb6SMauro Carvalho Chehab 136023aabb6SMauro Carvalho Chehab def addError(self, test, err): 137023aabb6SMauro Carvalho Chehab super().addError(test, err) 138023aabb6SMauro Carvalho Chehab self._record_test(test, "ERROR") 139023aabb6SMauro Carvalho Chehab 140023aabb6SMauro Carvalho Chehab def addSkip(self, test, reason): 141023aabb6SMauro Carvalho Chehab super().addSkip(test, reason) 142023aabb6SMauro Carvalho Chehab self._record_test(test, f"SKIP ({reason})") 143023aabb6SMauro Carvalho Chehab 144*07f6cb18SMauro Carvalho Chehab def printResults(self, verbose): 145023aabb6SMauro Carvalho Chehab """ 146023aabb6SMauro Carvalho Chehab Print results using colors if tty. 147023aabb6SMauro Carvalho Chehab """ 148023aabb6SMauro Carvalho Chehab # Check for ANSI color support 149023aabb6SMauro Carvalho Chehab use_color = sys.stdout.isatty() 150023aabb6SMauro Carvalho Chehab COLORS = { 151023aabb6SMauro Carvalho Chehab "OK": "\033[32m", # Green 152023aabb6SMauro Carvalho Chehab "FAIL": "\033[31m", # Red 153023aabb6SMauro Carvalho Chehab "SKIP": "\033[1;33m", # Yellow 154023aabb6SMauro Carvalho Chehab "PARTIAL": "\033[33m", # Orange 155023aabb6SMauro Carvalho Chehab "EXPECTED_FAIL": "\033[36m", # Cyan 156023aabb6SMauro Carvalho Chehab "reset": "\033[0m", # Reset to default terminal color 157023aabb6SMauro Carvalho Chehab } 158023aabb6SMauro Carvalho Chehab if not use_color: 159023aabb6SMauro Carvalho Chehab for c in COLORS: 160023aabb6SMauro Carvalho Chehab COLORS[c] = "" 161023aabb6SMauro Carvalho Chehab 162023aabb6SMauro Carvalho Chehab # Calculate maximum test name length 163023aabb6SMauro Carvalho Chehab if not self.test_results: 164023aabb6SMauro Carvalho Chehab return 165023aabb6SMauro Carvalho Chehab try: 166023aabb6SMauro Carvalho Chehab lengths = [] 167023aabb6SMauro Carvalho Chehab for module in self.test_results.values(): 168023aabb6SMauro Carvalho Chehab for tests in module.values(): 169023aabb6SMauro Carvalho Chehab for test_name, _ in tests: 170023aabb6SMauro Carvalho Chehab lengths.append(len(test_name) + 1) # +1 for colon 171023aabb6SMauro Carvalho Chehab max_length = max(lengths) + 2 # Additional padding 172023aabb6SMauro Carvalho Chehab except ValueError: 173023aabb6SMauro Carvalho Chehab sys.exit("Test list is empty") 174023aabb6SMauro Carvalho Chehab 175023aabb6SMauro Carvalho Chehab # Print results 176023aabb6SMauro Carvalho Chehab for module_name, classes in self.test_results.items(): 177*07f6cb18SMauro Carvalho Chehab if verbose: 178023aabb6SMauro Carvalho Chehab print(f"{module_name}:") 179023aabb6SMauro Carvalho Chehab for class_name, tests in classes.items(): 180*07f6cb18SMauro Carvalho Chehab if verbose: 181023aabb6SMauro Carvalho Chehab print(f" {class_name}:") 182023aabb6SMauro Carvalho Chehab for test_name, status in tests: 183*07f6cb18SMauro Carvalho Chehab if not verbose and status in [ "OK", "EXPECTED_FAIL" ]: 184*07f6cb18SMauro Carvalho Chehab continue 185*07f6cb18SMauro Carvalho Chehab 186023aabb6SMauro Carvalho Chehab # Get base status without reason for SKIP 187023aabb6SMauro Carvalho Chehab if status.startswith("SKIP"): 188023aabb6SMauro Carvalho Chehab status_code = status.split()[0] 189023aabb6SMauro Carvalho Chehab else: 190023aabb6SMauro Carvalho Chehab status_code = status 191023aabb6SMauro Carvalho Chehab color = COLORS.get(status_code, "") 192023aabb6SMauro Carvalho Chehab print( 193023aabb6SMauro Carvalho Chehab f" {test_name + ':':<{max_length}}{color}{status}{COLORS['reset']}" 194023aabb6SMauro Carvalho Chehab ) 195*07f6cb18SMauro Carvalho Chehab if verbose: 196023aabb6SMauro Carvalho Chehab print() 197023aabb6SMauro Carvalho Chehab 198023aabb6SMauro Carvalho Chehab # Print summary 199023aabb6SMauro Carvalho Chehab print(f"\nRan {self.testsRun} tests", end="") 200023aabb6SMauro Carvalho Chehab if hasattr(self, "timeTaken"): 201023aabb6SMauro Carvalho Chehab print(f" in {self.timeTaken:.3f}s", end="") 202023aabb6SMauro Carvalho Chehab print() 203023aabb6SMauro Carvalho Chehab 204023aabb6SMauro Carvalho Chehab if not self.wasSuccessful(): 205023aabb6SMauro Carvalho Chehab print(f"\n{COLORS['FAIL']}FAILED (", end="") 206023aabb6SMauro Carvalho Chehab failures = getattr(self, "failures", []) 207023aabb6SMauro Carvalho Chehab errors = getattr(self, "errors", []) 208023aabb6SMauro Carvalho Chehab if failures: 209023aabb6SMauro Carvalho Chehab print(f"failures={len(failures)}", end="") 210023aabb6SMauro Carvalho Chehab if errors: 211023aabb6SMauro Carvalho Chehab if failures: 212023aabb6SMauro Carvalho Chehab print(", ", end="") 213023aabb6SMauro Carvalho Chehab print(f"errors={len(errors)}", end="") 214023aabb6SMauro Carvalho Chehab print(f"){COLORS['reset']}") 215023aabb6SMauro Carvalho Chehab 216023aabb6SMauro Carvalho Chehab 217023aabb6SMauro Carvalho Chehabdef flatten_suite(suite): 218023aabb6SMauro Carvalho Chehab """Flatten test suite hierarchy.""" 219023aabb6SMauro Carvalho Chehab tests = [] 220023aabb6SMauro Carvalho Chehab for item in suite: 221023aabb6SMauro Carvalho Chehab if isinstance(item, unittest.TestSuite): 222023aabb6SMauro Carvalho Chehab tests.extend(flatten_suite(item)) 223023aabb6SMauro Carvalho Chehab else: 224023aabb6SMauro Carvalho Chehab tests.append(item) 225023aabb6SMauro Carvalho Chehab return tests 226023aabb6SMauro Carvalho Chehab 227023aabb6SMauro Carvalho Chehab 228023aabb6SMauro Carvalho Chehabclass TestUnits: 229023aabb6SMauro Carvalho Chehab """ 230023aabb6SMauro Carvalho Chehab Helper class to set verbosity level. 231023aabb6SMauro Carvalho Chehab 232023aabb6SMauro Carvalho Chehab This class discover test files, import its unittest classes and 233023aabb6SMauro Carvalho Chehab executes the test on it. 234023aabb6SMauro Carvalho Chehab """ 235023aabb6SMauro Carvalho Chehab def parse_args(self): 236023aabb6SMauro Carvalho Chehab """Returns a parser for command line arguments.""" 237023aabb6SMauro Carvalho Chehab parser = argparse.ArgumentParser(description="Test runner with regex filtering") 238023aabb6SMauro Carvalho Chehab parser.add_argument("-v", "--verbose", action="count", default=1) 239*07f6cb18SMauro Carvalho Chehab parser.add_argument("-q", "--quiet", action="store_true") 240023aabb6SMauro Carvalho Chehab parser.add_argument("-f", "--failfast", action="store_true") 241023aabb6SMauro Carvalho Chehab parser.add_argument("-k", "--keyword", 242023aabb6SMauro Carvalho Chehab help="Regex pattern to filter test methods") 243023aabb6SMauro Carvalho Chehab return parser 244023aabb6SMauro Carvalho Chehab 245023aabb6SMauro Carvalho Chehab def run(self, caller_file=None, pattern=None, 246023aabb6SMauro Carvalho Chehab suite=None, parser=None, args=None, env=None): 247023aabb6SMauro Carvalho Chehab """ 248023aabb6SMauro Carvalho Chehab Execute all tests from the unity test file. 249023aabb6SMauro Carvalho Chehab 250023aabb6SMauro Carvalho Chehab It contains several optional parameters: 251023aabb6SMauro Carvalho Chehab 252023aabb6SMauro Carvalho Chehab ``caller_file``: 253023aabb6SMauro Carvalho Chehab - name of the file that contains test. 254023aabb6SMauro Carvalho Chehab 255023aabb6SMauro Carvalho Chehab typical usage is to place __file__ at the caller test, e.g.:: 256023aabb6SMauro Carvalho Chehab 257023aabb6SMauro Carvalho Chehab if __name__ == "__main__": 258023aabb6SMauro Carvalho Chehab TestUnits().run(__file__) 259023aabb6SMauro Carvalho Chehab 260023aabb6SMauro Carvalho Chehab ``pattern``: 261023aabb6SMauro Carvalho Chehab - optional pattern to match multiple file names. Defaults 262023aabb6SMauro Carvalho Chehab to basename of ``caller_file``. 263023aabb6SMauro Carvalho Chehab 264023aabb6SMauro Carvalho Chehab ``suite``: 265023aabb6SMauro Carvalho Chehab - an unittest suite initialized by the caller using 266023aabb6SMauro Carvalho Chehab ``unittest.TestLoader().discover()``. 267023aabb6SMauro Carvalho Chehab 268023aabb6SMauro Carvalho Chehab ``parser``: 269023aabb6SMauro Carvalho Chehab - an argparse parser. If not defined, this helper will create 270023aabb6SMauro Carvalho Chehab one. 271023aabb6SMauro Carvalho Chehab 272023aabb6SMauro Carvalho Chehab ``args``: 273023aabb6SMauro Carvalho Chehab - an ``argparse.Namespace`` data filled by the caller. 274023aabb6SMauro Carvalho Chehab 275023aabb6SMauro Carvalho Chehab ``env``: 276023aabb6SMauro Carvalho Chehab - environment variables that will be passed to the test suite 277023aabb6SMauro Carvalho Chehab 278023aabb6SMauro Carvalho Chehab At least ``caller_file`` or ``suite`` must be used, otherwise a 279023aabb6SMauro Carvalho Chehab ``TypeError`` will be raised. 280023aabb6SMauro Carvalho Chehab """ 281023aabb6SMauro Carvalho Chehab if not args: 282023aabb6SMauro Carvalho Chehab if not parser: 283023aabb6SMauro Carvalho Chehab parser = self.parse_args() 284023aabb6SMauro Carvalho Chehab args = parser.parse_args() 285023aabb6SMauro Carvalho Chehab 286023aabb6SMauro Carvalho Chehab if not caller_file and not suite: 287023aabb6SMauro Carvalho Chehab raise TypeError("Either caller_file or suite is needed at TestUnits") 288023aabb6SMauro Carvalho Chehab 289*07f6cb18SMauro Carvalho Chehab if args.quiet: 290*07f6cb18SMauro Carvalho Chehab verbose = 0 291*07f6cb18SMauro Carvalho Chehab else: 292023aabb6SMauro Carvalho Chehab verbose = args.verbose 293023aabb6SMauro Carvalho Chehab 294023aabb6SMauro Carvalho Chehab if not env: 295023aabb6SMauro Carvalho Chehab env = os.environ.copy() 296023aabb6SMauro Carvalho Chehab 297023aabb6SMauro Carvalho Chehab env["VERBOSE"] = f"{verbose}" 298023aabb6SMauro Carvalho Chehab 299023aabb6SMauro Carvalho Chehab patcher = patch.dict(os.environ, env) 300023aabb6SMauro Carvalho Chehab patcher.start() 301023aabb6SMauro Carvalho Chehab # ensure it gets stopped after 302023aabb6SMauro Carvalho Chehab atexit.register(patcher.stop) 303023aabb6SMauro Carvalho Chehab 304023aabb6SMauro Carvalho Chehab 305023aabb6SMauro Carvalho Chehab if verbose >= 2: 306023aabb6SMauro Carvalho Chehab unittest.TextTestRunner(verbosity=verbose).run = lambda suite: suite 307023aabb6SMauro Carvalho Chehab 308023aabb6SMauro Carvalho Chehab # Load ONLY tests from the calling file 309023aabb6SMauro Carvalho Chehab if not suite: 310023aabb6SMauro Carvalho Chehab if not pattern: 311023aabb6SMauro Carvalho Chehab pattern = caller_file 312023aabb6SMauro Carvalho Chehab 313023aabb6SMauro Carvalho Chehab loader = unittest.TestLoader() 314023aabb6SMauro Carvalho Chehab suite = loader.discover(start_dir=os.path.dirname(caller_file), 315023aabb6SMauro Carvalho Chehab pattern=os.path.basename(caller_file)) 316023aabb6SMauro Carvalho Chehab 317023aabb6SMauro Carvalho Chehab # Flatten the suite for environment injection 318023aabb6SMauro Carvalho Chehab tests_to_inject = flatten_suite(suite) 319023aabb6SMauro Carvalho Chehab 320023aabb6SMauro Carvalho Chehab # Filter tests by method name if -k specified 321023aabb6SMauro Carvalho Chehab if args.keyword: 322023aabb6SMauro Carvalho Chehab try: 323023aabb6SMauro Carvalho Chehab pattern = re.compile(args.keyword) 324023aabb6SMauro Carvalho Chehab filtered_suite = unittest.TestSuite() 325023aabb6SMauro Carvalho Chehab for test in tests_to_inject: # Use the pre-flattened list 326023aabb6SMauro Carvalho Chehab method_name = test.id().split(".")[-1] 327023aabb6SMauro Carvalho Chehab if pattern.search(method_name): 328023aabb6SMauro Carvalho Chehab filtered_suite.addTest(test) 329023aabb6SMauro Carvalho Chehab suite = filtered_suite 330023aabb6SMauro Carvalho Chehab except re.error as e: 331023aabb6SMauro Carvalho Chehab sys.stderr.write(f"Invalid regex pattern: {e}\n") 332023aabb6SMauro Carvalho Chehab sys.exit(1) 333023aabb6SMauro Carvalho Chehab else: 334023aabb6SMauro Carvalho Chehab # Maintain original suite structure if no keyword filtering 335023aabb6SMauro Carvalho Chehab suite = unittest.TestSuite(tests_to_inject) 336023aabb6SMauro Carvalho Chehab 337023aabb6SMauro Carvalho Chehab if verbose >= 2: 338023aabb6SMauro Carvalho Chehab resultclass = None 339023aabb6SMauro Carvalho Chehab else: 340023aabb6SMauro Carvalho Chehab resultclass = Summary 341023aabb6SMauro Carvalho Chehab 342023aabb6SMauro Carvalho Chehab runner = unittest.TextTestRunner(verbosity=args.verbose, 343023aabb6SMauro Carvalho Chehab resultclass=resultclass, 344023aabb6SMauro Carvalho Chehab failfast=args.failfast) 345023aabb6SMauro Carvalho Chehab result = runner.run(suite) 346023aabb6SMauro Carvalho Chehab if resultclass: 347*07f6cb18SMauro Carvalho Chehab result.printResults(verbose) 348023aabb6SMauro Carvalho Chehab 349023aabb6SMauro Carvalho Chehab sys.exit(not result.wasSuccessful()) 350023aabb6SMauro Carvalho Chehab 351023aabb6SMauro Carvalho Chehab 352023aabb6SMauro Carvalho Chehabdef run_unittest(fname): 353023aabb6SMauro Carvalho Chehab """ 354023aabb6SMauro Carvalho Chehab Basic usage of TestUnits class. 355023aabb6SMauro Carvalho Chehab 356023aabb6SMauro Carvalho Chehab Use it when there's no need to pass any extra argument to the tests 357023aabb6SMauro Carvalho Chehab with. The recommended way is to place this at the end of each 358023aabb6SMauro Carvalho Chehab unittest module:: 359023aabb6SMauro Carvalho Chehab 360023aabb6SMauro Carvalho Chehab if __name__ == "__main__": 361023aabb6SMauro Carvalho Chehab run_unittest(__file__) 362023aabb6SMauro Carvalho Chehab """ 363023aabb6SMauro Carvalho Chehab TestUnits().run(fname) 364