[Lldb-commits] [lldb] [llvm] [lit] Enforce read-only behavior on lit_config.maxIndividualTestTime (PR #198193)

Vitaly Buka via lldb-commits lldb-commits at lists.llvm.org
Tue May 26 22:48:17 PDT 2026


https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/198193

>From 6ae15a11245f43273582497e2446500c78ce1b90 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Sun, 17 May 2026 10:10:56 -0700
Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
 =?UTF-8?q?anges=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7

[skip ci]
---
 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           | 20 +++++++++++++++++++
 llvm/utils/lit/lit/formats/googletest.py      |  4 ++--
 llvm/utils/lit/lit/main.py                    | 13 +-----------
 .../tests/Inputs/googletest-timeout/lit.cfg   |  2 +-
 .../lit/tests/Inputs/shtest-timeout/lit.cfg   |  2 +-
 .../lit/tests/Inputs/timeout-config/lit.cfg   | 13 ++++++++++++
 .../lit/tests/Inputs/timeout-config/test.py   |  1 +
 llvm/utils/lit/tests/timeout-config.py        |  9 +++++++++
 14 files changed, 60 insertions(+), 25 deletions(-)
 create mode 100644 llvm/utils/lit/tests/Inputs/timeout-config/lit.cfg
 create mode 100644 llvm/utils/lit/tests/Inputs/timeout-config/test.py
 create mode 100644 llvm/utils/lit/tests/timeout-config.py

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 c92b104c9227c..d50bc4ce3e03f 100644
--- a/lldb/test/API/lit.cfg.py
+++ b/lldb/test/API/lit.cfg.py
@@ -192,7 +192,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 64107001e3aae..9e7f6cad9696d 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 4be2a0f6d8121..6c14c943d6dcc 100644
--- a/llvm/utils/lit/lit/LitConfig.py
+++ b/llvm/utils/lit/lit/LitConfig.py
@@ -36,7 +36,7 @@ def __init__(
         order,
         params,
         config_prefix=None,
-        maxIndividualTestTime=0,
+        maxIndividualTestTime=None,
         maxRetriesPerTest=None,
         parallelism_groups={},
         per_test_coverage=False,
@@ -124,6 +124,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 82852f1852705..2c14aaeb8f2f4 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 4bed1c95decd4..3efe670c26269 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.
@@ -226,6 +229,7 @@ def recursiveExpansionLimit(self, value):
             )
         self._recursiveExpansionLimit = value
 
+
     def finish(self, litConfig):
         """finish() - Finish this config object, after loading is complete."""
 
@@ -247,6 +251,22 @@ def finish(self, litConfig):
             and getattr(self, "test_retry_attempts", None) is None
         ):
             self.test_retry_attempts = litConfig.maxRetriesPerTest
+        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 01820da38c954..75dee5859a27e 100644
--- a/llvm/utils/lit/lit/formats/googletest.py
+++ b/llvm/utils/lit/lit/formats/googletest.py
@@ -192,7 +192,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:
@@ -200,7 +200,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"]
diff --git a/llvm/utils/lit/tests/Inputs/timeout-config/lit.cfg b/llvm/utils/lit/tests/Inputs/timeout-config/lit.cfg
new file mode 100644
index 0000000000000..1611667409f61
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/timeout-config/lit.cfg
@@ -0,0 +1,13 @@
+# -*- Python -*-
+import os
+import sys
+import lit.formats
+
+config.name = "timeout-config"
+config.test_format = lit.formats.ShTest()
+config.suffixes = [".py"]
+config.test_source_root = os.path.dirname(__file__)
+config.test_exec_root = config.test_source_root
+config.maxIndividualTestTime = 1
+
+config.substitutions.append(("%{python}", '"%s"' % (sys.executable)))
diff --git a/llvm/utils/lit/tests/Inputs/timeout-config/test.py b/llvm/utils/lit/tests/Inputs/timeout-config/test.py
new file mode 100644
index 0000000000000..aaedc91a1ac38
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/timeout-config/test.py
@@ -0,0 +1 @@
+# RUN: %{python} -c "import time; time.sleep(10)"
diff --git a/llvm/utils/lit/tests/timeout-config.py b/llvm/utils/lit/tests/timeout-config.py
new file mode 100644
index 0000000000000..3305a606701d2
--- /dev/null
+++ b/llvm/utils/lit/tests/timeout-config.py
@@ -0,0 +1,9 @@
+# REQUIRES: lit-max-individual-test-time
+# UNSUPPORTED: system-windows
+
+# RUN: not %{lit} \
+# RUN:   %{inputs}/timeout-config \
+# RUN:   -j 1 -v > %t.out 2> %t.err
+# RUN: FileCheck < %t.out %s
+
+# CHECK: TIMEOUT: timeout-config :: test.py

>From 3597ec5cf4b1a8fb7d82327ce0331ddc72f3302f Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Sun, 17 May 2026 11:42:54 -0700
Subject: [PATCH 2/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
 =?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7

[skip ci]
---
 llvm/utils/lit/lit/TestingConfig.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/utils/lit/lit/TestingConfig.py b/llvm/utils/lit/lit/TestingConfig.py
index ade70bb31b1b8..3d42cfe304d6f 100644
--- a/llvm/utils/lit/lit/TestingConfig.py
+++ b/llvm/utils/lit/lit/TestingConfig.py
@@ -250,6 +250,7 @@ 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:

>From 5954e212a9138c5a18219ba3c1f486e89114467e Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 26 May 2026 22:48:06 -0700
Subject: [PATCH 3/3] feedback addressed

Created using spr 1.3.7
---
 llvm/utils/lit/tests/Inputs/lit-config-readonly/dummy.txt | 1 -
 llvm/utils/lit/tests/Inputs/lit-config-readonly/lit.cfg   | 1 -
 llvm/utils/lit/tests/lit-config-readonly.py               | 2 +-
 3 files changed, 1 insertion(+), 3 deletions(-)
 delete mode 100644 llvm/utils/lit/tests/Inputs/lit-config-readonly/dummy.txt

diff --git a/llvm/utils/lit/tests/Inputs/lit-config-readonly/dummy.txt b/llvm/utils/lit/tests/Inputs/lit-config-readonly/dummy.txt
deleted file mode 100644
index 18f9fae2ad451..0000000000000
--- a/llvm/utils/lit/tests/Inputs/lit-config-readonly/dummy.txt
+++ /dev/null
@@ -1 +0,0 @@
-# dummy test
diff --git a/llvm/utils/lit/tests/Inputs/lit-config-readonly/lit.cfg b/llvm/utils/lit/tests/Inputs/lit-config-readonly/lit.cfg
index 14940dfc0ec34..39d3396b29d34 100644
--- a/llvm/utils/lit/tests/Inputs/lit-config-readonly/lit.cfg
+++ b/llvm/utils/lit/tests/Inputs/lit-config-readonly/lit.cfg
@@ -1,4 +1,3 @@
-# -*- Python -*-
 import os
 import lit.formats
 
diff --git a/llvm/utils/lit/tests/lit-config-readonly.py b/llvm/utils/lit/tests/lit-config-readonly.py
index 3ecf693b5730c..e15e79b9668bc 100644
--- a/llvm/utils/lit/tests/lit-config-readonly.py
+++ b/llvm/utils/lit/tests/lit-config-readonly.py
@@ -1,4 +1,4 @@
-# RUN: not %{lit} %{inputs}/lit-config-readonly > %t.out 2> %t.err
+# RUN: not %{lit} %{inputs}/lit-config-readonly 2> %t.err
 # RUN: FileCheck --check-prefix=CHECK-ERR < %t.err %s
 
 # CHECK-ERR: AttributeError: lit_config.maxIndividualTestTime is read-only. Use config.maxIndividualTestTime instead.



More information about the lldb-commits mailing list