xref: /linux/tools/lib/python/unittest_helper.py (revision 023aabb6ccb298add344cab7c00c5f27b10319aa)
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