[Concept,02/20] u_boot_pylib: Support passing modules to run_test_suites()

Message ID 20260316154733.1587261-3-sjg@u-boot.org
State New
Headers
Series buildman: Add distributed builds |

Commit Message

Simon Glass March 16, 2026, 3:47 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

Allow passing a module object to run_test_suites() in addition to test
classes and doctest module name strings. When a module is passed, all
TestCase subclasses are extracted automatically.

This avoids having to enumerate every test class when registering a test
module.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 tools/u_boot_pylib/test_util.py | 49 ++++++++++++++++++++++-----------
 1 file changed, 33 insertions(+), 16 deletions(-)
  

Patch

diff --git a/tools/u_boot_pylib/test_util.py b/tools/u_boot_pylib/test_util.py
index b1c8740d883..90ae0cee6ce 100644
--- a/tools/u_boot_pylib/test_util.py
+++ b/tools/u_boot_pylib/test_util.py
@@ -203,24 +203,41 @@  def run_test_suites(toolname, debug, verbosity, no_capture, test_preserve_dirs,
         if isinstance(module, str) and (not test_name or test_name == module):
             suite.addTests(doctest.DocTestSuite(module))
 
-    for module in class_and_module_list:
-        if isinstance(module, str):
+    for entry in class_and_module_list:
+        if isinstance(entry, str):
             continue
-        # Test the test module about our arguments, if it is interested
-        if hasattr(module, 'setup_test_args'):
-            setup_test_args = getattr(module, 'setup_test_args')
-            setup_test_args(preserve_indir=test_preserve_dirs,
-                preserve_outdirs=test_preserve_dirs and test_name is not None,
-                toolpath=toolpath, verbosity=verbosity, no_capture=no_capture)
-        if test_name:
-            # Since Python v3.5 If an ImportError or AttributeError occurs
-            # while traversing a name then a synthetic test that raises that
-            # error when run will be returned. Check that the requested test
-            # exists, otherwise these errors are included in the results.
-            if test_name in loader.getTestCaseNames(module):
-                suite.addTests(loader.loadTestsFromName(test_name, module))
+
+        # If entry is a module, extract all TestCase subclasses from it
+        if hasattr(entry, '__file__'):
+            classes = [obj for obj in vars(entry).values()
+                       if (isinstance(obj, type)
+                           and issubclass(obj, unittest.TestCase)
+                           and obj is not unittest.TestCase)]
         else:
-            suite.addTests(loader.loadTestsFromTestCase(module))
+            classes = [entry]
+
+        for module in classes:
+            # Tell the test module about our arguments, if interested
+            if hasattr(module, 'setup_test_args'):
+                setup_test_args = getattr(module, 'setup_test_args')
+                setup_test_args(
+                    preserve_indir=test_preserve_dirs,
+                    preserve_outdirs=(test_preserve_dirs
+                                     and test_name is not None),
+                    toolpath=toolpath, verbosity=verbosity,
+                    no_capture=no_capture)
+            if test_name:
+                # Since Python v3.5 If an ImportError or
+                # AttributeError occurs while traversing a name then
+                # a synthetic test that raises that error when run
+                # will be returned. Check that the requested test
+                # exists, otherwise these errors are included in the
+                # results.
+                if test_name in loader.getTestCaseNames(module):
+                    suite.addTests(
+                        loader.loadTestsFromName(test_name, module))
+            else:
+                suite.addTests(loader.loadTestsFromTestCase(module))
 
     print(f" Running {toolname} tests ".center(70, "="))
     result = runner.run(suite)