[Lldb-commits] [lldb] [llvm] [lit] Move maxIndividualTestTime from global to test suite config (PR #199996)

Vitaly Buka via lldb-commits lldb-commits at lists.llvm.org
Wed May 27 07:40:28 PDT 2026


https://github.com/vitalybuka created https://github.com/llvm/llvm-project/pull/199996

Simplify LitConfig initialization and setter to allow None values.
TestingConfig.maxIndividualTestTime is initialized to 0 (or resolved to
0 if None) strictly during initialization.

This fixes an issue where the aggressive BOLT timeout of 60s (previously
set globally on lit_config) was leaking and affecting libc++ tests. By
moving the timeout configuration from the global lit_config to the
individual test suite config, we ensure that timeouts are isolated and
respect suite-local settings without leaking.

PR Stack:
* ➤ https://github.com/llvm/llvm-project/pull/198192
* https://github.com/llvm/llvm-project/pull/198193

Assisted-by: Gemini


>From 632e25f8313033a141e25f788ded9c4154a782f0 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 26 May 2026 22:26:51 -0700
Subject: [PATCH] [lit] Move maxIndividualTestTime from global to test suite
 config
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Simplify LitConfig initialization and setter to allow None values.
TestingConfig.maxIndividualTestTime is initialized to 0 (or resolved to
0 if None) strictly during initialization.

This fixes an issue where the aggressive BOLT timeout of 60s (previously
set globally on lit_config) was leaking and affecting libc++ tests. By
moving the timeout configuration from the global lit_config to the
individual test suite config, we ensure that timeouts are isolated and
respect suite-local settings without leaking.

PR Stack:
* ➤ https://github.com/llvm/llvm-project/pull/198192
* https://github.com/llvm/llvm-project/pull/198193

Assisted-by: Gemini
---
 bolt/test/lit.cfg.py                             |  2 +-
 libsycl/test/lit.cfg.py                          |  2 +-
 lldb/test/API/lit.cfg.py                         |  2 +-
 lldb/test/Shell/lit.cfg.py                       |  2 +-
 llvm/utils/lit/lit/LitConfig.py                  |  5 ++++-
 llvm/utils/lit/lit/TestRunner.py                 |  8 ++++----
 llvm/utils/lit/lit/TestingConfig.py              | 16 ++++++++++++++++
 llvm/utils/lit/lit/formats/googletest.py         |  4 ++--
 llvm/utils/lit/lit/main.py                       | 13 +------------
 .../lit/tests/Inputs/googletest-timeout/lit.cfg  |  2 +-
 .../lit/tests/Inputs/shtest-timeout/lit.cfg      |  2 +-
 11 files changed, 33 insertions(+), 25 deletions(-)

diff --git a/bolt/test/lit.cfg.py b/bolt/test/lit.cfg.py
index 3299051db4983..a3a49444156b2 100644
--- a/bolt/test/lit.cfg.py
+++ b/bolt/test/lit.cfg.py
@@ -69,7 +69,7 @@
 supported, errormsg = lit_config.maxIndividualTestTimeIsSupported
 if supported:
     config.available_features.add("lit-max-individual-test-time")
-    lit_config.maxIndividualTestTime = 60
+    config.maxIndividualTestTime = 60
 else:
     lit_config.warning(
         "Setting a timeout per test not supported. "
diff --git a/libsycl/test/lit.cfg.py b/libsycl/test/lit.cfg.py
index 377be90791ef0..2610f23ba90a8 100644
--- a/libsycl/test/lit.cfg.py
+++ b/libsycl/test/lit.cfg.py
@@ -205,7 +205,7 @@ def __exit__(self, exc_type, exc_value, exc_traceback):
     import psutil
 
     # Set timeout for a single test
-    lit_config.maxIndividualTestTime = 600
+    config.maxIndividualTestTime = 600
 
 except ImportError:
     pass
diff --git a/lldb/test/API/lit.cfg.py b/lldb/test/API/lit.cfg.py
index 359d45a0c724e..2662a77199641 100644
--- a/lldb/test/API/lit.cfg.py
+++ b/lldb/test/API/lit.cfg.py
@@ -199,7 +199,7 @@ def delete_module_cache(path):
 # lit complains if the value is set but it is not supported.
 supported, errormsg = lit_config.maxIndividualTestTimeIsSupported
 if supported:
-    lit_config.maxIndividualTestTime = 600
+    config.maxIndividualTestTime = 600
 else:
     lit_config.warning("Could not set a default per-test timeout. " + errormsg)
 
diff --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py
index 3bff8b087c5a4..1b7532d12ce4a 100644
--- a/lldb/test/Shell/lit.cfg.py
+++ b/lldb/test/Shell/lit.cfg.py
@@ -120,7 +120,7 @@ def calculate_arch_features(arch_string):
 # lit complains if the value is set but it is not supported.
 supported, errormsg = lit_config.maxIndividualTestTimeIsSupported
 if supported:
-    lit_config.maxIndividualTestTime = 600
+    config.maxIndividualTestTime = 600
 else:
     lit_config.warning("Could not set a default per-test timeout. " + errormsg)
 
diff --git a/llvm/utils/lit/lit/LitConfig.py b/llvm/utils/lit/lit/LitConfig.py
index be8596f0ccdc4..4696a0fda1243 100644
--- a/llvm/utils/lit/lit/LitConfig.py
+++ b/llvm/utils/lit/lit/LitConfig.py
@@ -35,7 +35,7 @@ def __init__(
         order,
         params,
         config_prefix=None,
-        maxIndividualTestTime=0,
+        maxIndividualTestTime=None,
         maxRetriesPerTest=None,
         parallelism_groups={},
         per_test_coverage=False,
@@ -123,6 +123,9 @@ def maxIndividualTestTime(self, value):
         Interface for setting maximum time to spend executing
         a single test
         """
+        if value is None:
+            self._maxIndividualTestTime = None
+            return
         if not isinstance(value, int):
             self.fatal("maxIndividualTestTime must set to a value of type int.")
         self._maxIndividualTestTime = value
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 0159028f36723..66450f5fa6b81 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -703,7 +703,7 @@ def executeScriptInternal(
     shenv.env["LIT_CURRENT_TESTCASE"] = test.getFullName()
 
     exitCode, timeoutInfo = executeShCmd(
-        cmd, shenv, results, timeout=litConfig.maxIndividualTestTime
+        cmd, shenv, results, timeout=test.config.maxIndividualTestTime
     )
 
     out = err = ""
@@ -747,7 +747,7 @@ def executeScriptInternal(
 
         # If nothing interesting happened, move on.
         if (
-            litConfig.maxIndividualTestTime == 0
+            test.config.maxIndividualTestTime == 0
             and result.exitCode == 0
             and not result.stdout.strip()
             and not result.stderr.strip()
@@ -776,7 +776,7 @@ def executeScriptInternal(
             else:
                 codeStr = str(result.exitCode)
             out += "# error: command failed with exit status: %s\n" % (codeStr,)
-        if litConfig.maxIndividualTestTime > 0 and result.timeoutReached:
+        if test.config.maxIndividualTestTime > 0 and result.timeoutReached:
             out += "# error: command reached timeout: %s\n" % (
                 str(result.timeoutReached),
             )
@@ -900,7 +900,7 @@ def executeScript(
             command,
             cwd=cwd,
             env=env,
-            timeout=litConfig.maxIndividualTestTime,
+            timeout=test.config.maxIndividualTestTime,
         )
         return (out, err, exitCode, None, None)
     except lit.util.ExecuteCommandTimeoutException as e:
diff --git a/llvm/utils/lit/lit/TestingConfig.py b/llvm/utils/lit/lit/TestingConfig.py
index a6b4fa212ae35..7c7d51ddaf48d 100644
--- a/llvm/utils/lit/lit/TestingConfig.py
+++ b/llvm/utils/lit/lit/TestingConfig.py
@@ -125,6 +125,7 @@ def fromdefaults(litConfig):
             available_features=available_features,
             pipefail=True,
             standalone_tests=False,
+            maxIndividualTestTime=litConfig.maxIndividualTestTime,
         )
 
     def load_from_path(self, path, litConfig):
@@ -185,6 +186,7 @@ def __init__(
         is_early=False,
         parallelism_group=None,
         standalone_tests=False,
+        maxIndividualTestTime=0,
     ):
         self.parent = parent
         self.name = str(name)
@@ -199,6 +201,7 @@ def __init__(
         self.available_features = set(available_features)
         self.pipefail = pipefail
         self.standalone_tests = standalone_tests
+        self.maxIndividualTestTime = maxIndividualTestTime or 0
         # This list is used by TestRunner.py to restrict running only tests that
         # require one of the features in this list if this list is non-empty.
         # Configurations can set this list to restrict the set of tests to run.
@@ -247,6 +250,19 @@ def finish(self, litConfig):
             and getattr(self, "test_retry_attempts", None) is None
         ):
             self.test_retry_attempts = litConfig.maxRetriesPerTest
+        # Global config is from LIT_OPTS and must override site-specific settings.
+        if litConfig.maxIndividualTestTime is not None:
+            suite_timeout = self.maxIndividualTestTime
+            if suite_timeout > 0 and suite_timeout != litConfig.maxIndividualTestTime:
+                litConfig.note(
+                    (
+                        "The test suite {0!r} configuration requested an individual"
+                        " test timeout of {1} seconds but a timeout of {2} seconds was"
+                        " requested on the command line. Forcing timeout to be {2}"
+                        " seconds."
+                    ).format(self.name, suite_timeout, litConfig.maxIndividualTestTime)
+                )
+            self.maxIndividualTestTime = litConfig.maxIndividualTestTime
 
     @property
     def root(self):
diff --git a/llvm/utils/lit/lit/formats/googletest.py b/llvm/utils/lit/lit/formats/googletest.py
index 5d60da509bdb7..e567cce541beb 100644
--- a/llvm/utils/lit/lit/formats/googletest.py
+++ b/llvm/utils/lit/lit/formats/googletest.py
@@ -191,7 +191,7 @@ def get_shard_header(shard_env):
             out, _, exitCode = lit.util.executeCommand(
                 cmd,
                 env=test.config.environment,
-                timeout=litConfig.maxIndividualTestTime,
+                timeout=test.config.maxIndividualTestTime,
                 redirect_stderr=True,
             )
         except lit.util.ExecuteCommandTimeoutException as e:
@@ -199,7 +199,7 @@ def get_shard_header(shard_env):
             return (
                 lit.Test.TIMEOUT,
                 f"{shard_header}{stream_msg}Reached "
-                f"timeout of {litConfig.maxIndividualTestTime} seconds",
+                f"timeout of {test.config.maxIndividualTestTime} seconds",
             )
 
         if not os.path.exists(test.gtest_json_file):
diff --git a/llvm/utils/lit/lit/main.py b/llvm/utils/lit/lit/main.py
index d1e16b28bea26..a3bd153040a69 100755
--- a/llvm/utils/lit/lit/main.py
+++ b/llvm/utils/lit/lit/main.py
@@ -44,6 +44,7 @@ def main(builtin_params={}):
         gtest_sharding=opts.gtest_sharding,
         maxRetriesPerTest=opts.maxRetriesPerTest,
         update_tests=opts.update_tests,
+        maxIndividualTestTime=opts.maxIndividualTestTime,
     )
 
     discovered_tests = lit.discovery.find_tests_for_inputs(
@@ -68,18 +69,6 @@ def main(builtin_params={}):
         print(" ".join(sorted(features)))
         sys.exit(0)
 
-    # Command line overrides configuration for maxIndividualTestTime.
-    if opts.maxIndividualTestTime is not None:  # `not None` is important (default: 0)
-        if opts.maxIndividualTestTime != lit_config.maxIndividualTestTime:
-            lit_config.note(
-                (
-                    "The test suite configuration requested an individual"
-                    " test timeout of {0} seconds but a timeout of {1} seconds was"
-                    " requested on the command line. Forcing timeout to be {1}"
-                    " seconds."
-                ).format(lit_config.maxIndividualTestTime, opts.maxIndividualTestTime)
-            )
-            lit_config.maxIndividualTestTime = opts.maxIndividualTestTime
 
     determine_order(discovered_tests, opts.order)
 
diff --git a/llvm/utils/lit/tests/Inputs/googletest-timeout/lit.cfg b/llvm/utils/lit/tests/Inputs/googletest-timeout/lit.cfg
index c1eab40f47e8d..200347aaf85c7 100644
--- a/llvm/utils/lit/tests/Inputs/googletest-timeout/lit.cfg
+++ b/llvm/utils/lit/tests/Inputs/googletest-timeout/lit.cfg
@@ -8,4 +8,4 @@ config.environment["GTEST_FILTER"] = lit_config.params.get("gtest_filter")
 
 if configSetTimeout == "1":
     # Try setting the max individual test time in the configuration
-    lit_config.maxIndividualTestTime = 1
+    config.maxIndividualTestTime = 1
diff --git a/llvm/utils/lit/tests/Inputs/shtest-timeout/lit.cfg b/llvm/utils/lit/tests/Inputs/shtest-timeout/lit.cfg
index b78c0eac31d88..3a72f35024eb2 100644
--- a/llvm/utils/lit/tests/Inputs/shtest-timeout/lit.cfg
+++ b/llvm/utils/lit/tests/Inputs/shtest-timeout/lit.cfg
@@ -19,7 +19,7 @@ configSetTimeout = lit_config.params.get("set_timeout", "0")
 
 if configSetTimeout != "0":
     # Try setting the max individual test time in the configuration
-    lit_config.maxIndividualTestTime = int(configSetTimeout)
+    config.maxIndividualTestTime = int(configSetTimeout)
 
 config.test_format = lit.formats.ShTest(execute_external=externalShell)
 config.suffixes = [".py"]



More information about the lldb-commits mailing list