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