From d7345a4ba2a8e755aed0eed71aaad374e930a0f1 Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Fri, 27 Mar 2026 05:00:47 +0530 Subject: [PATCH 1/6] systemtests: add configurable timeout per test case --- changelog-entries/735.md | 1 + tools/tests/README.md | 3 +++ tools/tests/generate_reference_results.py | 10 ++++++---- tools/tests/systemtests.py | 10 ++++++---- tools/tests/systemtests/Systemtest.py | 7 ++++--- tools/tests/systemtests/TestSuite.py | 6 +++++- tools/tests/tests.yaml | 1 + 7 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 changelog-entries/735.md diff --git a/changelog-entries/735.md b/changelog-entries/735.md new file mode 100644 index 000000000..e86208f00 --- /dev/null +++ b/changelog-entries/735.md @@ -0,0 +1 @@ +- Add optional `timeout` field to `tests.yaml` entries for per-test timeout configuration. Overridable via `PRECICE_SYSTEMTEST_TIMEOUT` environment variable. diff --git a/tools/tests/README.md b/tools/tests/README.md index 5675c47b6..a841d3968 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -331,8 +331,11 @@ test_suites: - fluid-openfoam - solid-fenics reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz + timeout: 1200 ``` +The optional `timeout` field (in seconds) sets the maximum time for the solver run and fieldcompare phases of that specific case. If omitted, it defaults to `GLOBAL_TIMEOUT` (currently 900s, overridable via the `PRECICE_SYSTEMTEST_TIMEOUT` environment variable). + This defines two test suites, namely `openfoam_adapter_pr` and `openfoam_adapter_release`. Each of them defines which case combinations of which tutorials to run. ### Generate Reference Results diff --git a/tools/tests/generate_reference_results.py b/tools/tests/generate_reference_results.py index 055e7b31c..204dc2401 100644 --- a/tools/tests/generate_reference_results.py +++ b/tools/tests/generate_reference_results.py @@ -2,7 +2,7 @@ from metadata_parser.metdata import Tutorials, ReferenceResult from systemtests.TestSuite import TestSuites from systemtests.SystemtestArguments import SystemtestArguments -from systemtests.Systemtest import Systemtest +from systemtests.Systemtest import Systemtest, GLOBAL_TIMEOUT from pathlib import Path from typing import List from paths import PRECICE_TESTS_DIR, PRECICE_TUTORIAL_DIR @@ -108,10 +108,12 @@ def main(): for test_suite in test_suites: tutorials = test_suite.cases_of_tutorial.keys() for tutorial in tutorials: - for case, reference_result in zip( - test_suite.cases_of_tutorial[tutorial], test_suite.reference_results[tutorial]): + timeouts = test_suite.timeouts.get(tutorial, []) + for i, (case, reference_result) in enumerate(zip( + test_suite.cases_of_tutorial[tutorial], test_suite.reference_results[tutorial])): + timeout = timeouts[i] if i < len(timeouts) and timeouts[i] is not None else GLOBAL_TIMEOUT systemtests_to_run.add( - Systemtest(tutorial, build_args, case, reference_result)) + Systemtest(tutorial, build_args, case, reference_result, timeout=timeout)) reference_result_per_tutorial = {} current_time_string = datetime.now().strftime('%Y-%m-%d %H:%M:%S') diff --git a/tools/tests/systemtests.py b/tools/tests/systemtests.py index 8a37670eb..36ffbf3c6 100644 --- a/tools/tests/systemtests.py +++ b/tools/tests/systemtests.py @@ -2,7 +2,7 @@ import argparse from pathlib import Path from systemtests.SystemtestArguments import SystemtestArguments -from systemtests.Systemtest import Systemtest, display_systemtestresults_as_table +from systemtests.Systemtest import Systemtest, GLOBAL_TIMEOUT, display_systemtestresults_as_table from systemtests.TestSuite import TestSuites from metadata_parser.metdata import Tutorials, Case import logging @@ -58,10 +58,12 @@ def main(): for test_suite in test_suites_to_execute: tutorials = test_suite.cases_of_tutorial.keys() for tutorial in tutorials: - for case, reference_result in zip( - test_suite.cases_of_tutorial[tutorial], test_suite.reference_results[tutorial]): + timeouts = test_suite.timeouts.get(tutorial, []) + for i, (case, reference_result) in enumerate(zip( + test_suite.cases_of_tutorial[tutorial], test_suite.reference_results[tutorial])): + timeout = timeouts[i] if i < len(timeouts) and timeouts[i] is not None else GLOBAL_TIMEOUT systemtests_to_run.append( - Systemtest(tutorial, build_args, case, reference_result)) + Systemtest(tutorial, build_args, case, reference_result, timeout=timeout)) if not systemtests_to_run: raise RuntimeError("Did not find any Systemtests to execute.") diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index 6abc5a029..552f95d3d 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -19,7 +19,7 @@ import os -GLOBAL_TIMEOUT = 900 +GLOBAL_TIMEOUT = int(os.environ.get("PRECICE_SYSTEMTEST_TIMEOUT", 900)) SHORT_TIMEOUT = 10 @@ -134,6 +134,7 @@ class Systemtest: arguments: SystemtestArguments case_combination: CaseCombination reference_result: ReferenceResult + timeout: int = GLOBAL_TIMEOUT params_to_use: Dict[str, str] = field(init=False) env: Dict[str, str] = field(init=False) @@ -394,7 +395,7 @@ def _run_field_compare(self): cwd=self.system_test_dir) try: - stdout, stderr = process.communicate(timeout=GLOBAL_TIMEOUT) + stdout, stderr = process.communicate(timeout=self.timeout) except KeyboardInterrupt as k: process.kill() raise KeyboardInterrupt from k @@ -483,7 +484,7 @@ def _run_tutorial(self): cwd=self.system_test_dir) try: - stdout, stderr = process.communicate(timeout=GLOBAL_TIMEOUT) + stdout, stderr = process.communicate(timeout=self.timeout) except KeyboardInterrupt as k: process.kill() # process.send_signal(9) diff --git a/tools/tests/systemtests/TestSuite.py b/tools/tests/systemtests/TestSuite.py index 9d8c2ac72..c2790eddd 100644 --- a/tools/tests/systemtests/TestSuite.py +++ b/tools/tests/systemtests/TestSuite.py @@ -10,6 +10,7 @@ class TestSuite: name: str cases_of_tutorial: Dict[Tutorial, List[CaseCombination]] reference_results: Dict[Tutorial, List[ReferenceResult]] + timeouts: Dict[Tutorial, List] = field(default_factory=dict) def __repr__(self) -> str: return_string = f"Test suite: {self.name} contains:" @@ -48,6 +49,7 @@ def from_yaml(cls, path, parsed_tutorials: Tutorials): for test_suite_name in test_suites_raw: case_combinations_of_tutorial = {} reference_results_of_tutorial = {} + timeouts_of_tutorial = {} # iterate over tutorials: for tutorial_case in test_suites_raw[test_suite_name]['tutorials']: tutorial = parsed_tutorials.get_by_path(tutorial_case['path']) @@ -57,6 +59,7 @@ def from_yaml(cls, path, parsed_tutorials: Tutorials): if tutorial not in case_combinations_of_tutorial: case_combinations_of_tutorial[tutorial] = [] reference_results_of_tutorial[tutorial] = [] + timeouts_of_tutorial[tutorial] = [] all_case_combinations = tutorial.case_combinations case_combination_requested = CaseCombination.from_string_list( @@ -65,12 +68,13 @@ def from_yaml(cls, path, parsed_tutorials: Tutorials): case_combinations_of_tutorial[tutorial].append(case_combination_requested) reference_results_of_tutorial[tutorial].append(ReferenceResult( tutorial_case['reference_result'], case_combination_requested)) + timeouts_of_tutorial[tutorial].append(tutorial_case.get('timeout', None)) else: raise Exception( f"Could not find the following cases {tutorial_case['case-combination']} in the current metadata of tutorial {tutorial.name}") testsuites.append(TestSuite(test_suite_name, case_combinations_of_tutorial, - reference_results_of_tutorial)) + reference_results_of_tutorial, timeouts_of_tutorial)) return cls(testsuites) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index fce38ea38..5b23d3e5d 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -32,6 +32,7 @@ test_suites: - fluid-openfoam - solid-nutils reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz + timeout: 1200 calculix_test: tutorials: - path: perpendicular-flap From ae4e72db2e5abbdef7cf5c8014d08541140c6e62 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 12 May 2026 19:32:15 +0200 Subject: [PATCH 2/6] Revert demonstration changes in tests.yaml --- tools/tests/tests.yaml | 65 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 5b23d3e5d..1cac21867 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -1,6 +1,15 @@ test_suites: + quickstart_test: + tutorials: + - &quickstart + path: quickstart + case_combination: + - fluid-openfoam + - solid-cpp + reference_result: ./quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz openfoam_adapter_pr: tutorials: + - *quickstart - path: flow-over-heated-plate case_combination: - fluid-openfoam @@ -11,6 +20,16 @@ test_suites: - fluid-openfoam - solid-calculix reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz + - path: perpendicular-flap + case_combination: + - fluid-openfoam + - solid-openfoam + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - path: flow-over-heated-plate-nearest-projection + case_combination: + - fluid-openfoam + - solid-openfoam + reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz openfoam_adapter_release: tutorials: - path: flow-over-heated-plate @@ -25,6 +44,11 @@ test_suites: - fluid-openfoam - solid-fenics reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz + - path: perpendicular-flap + case_combination: + - fluid-openfoam + - solid-fenics + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz nutils_test: tutorials: - path: flow-over-heated-plate @@ -32,7 +56,6 @@ test_suites: - fluid-openfoam - solid-nutils reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz - timeout: 1200 calculix_test: tutorials: - path: perpendicular-flap @@ -46,12 +69,19 @@ test_suites: case_combination: - macro-dumux - micro-dumux - reference_result: ./two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz + reference_result: ./two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz # Too small values, expected to fail the comparisons. - path: free-flow-over-porous-media case_combination: - free-flow-dumux - porous-media-dumux reference_result: ./free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz + micro_manager_test: + tutorials: + - path: two-scale-heat-conduction + case_combination: + - macro-dumux + - micro-dumux + reference_result: ./two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz # Too small values, expected to fail the comparisons. su2_test: tutorials: - path: perpendicular-flap @@ -59,6 +89,15 @@ test_suites: - fluid-su2 - solid-fenics reference_result: ./perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz + heat_exchanger_simplified_test: + tutorials: + - &heat_exchanger_simplified + path: heat-exchanger-simplified + case_combination: + - fluid-top-openfoam + - fluid-bottom-openfoam + - solid-calculix + reference_result: ./heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz dealii_test: tutorials: - path: perpendicular-flap @@ -66,6 +105,21 @@ test_suites: - fluid-openfoam - solid-dealii reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz + - path: perpendicular-flap + case_combination: + - fluid-openfoam + - solid-openfoam + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - path: perpendicular-flap + case_combination: + - fluid-openfoam + - solid-fenics + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz + - path: flow-over-heated-plate-nearest-projection + case_combination: + - fluid-openfoam + - solid-openfoam + reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz - path: multiple-perpendicular-flaps case_combination: - fluid-openfoam @@ -91,6 +145,7 @@ test_suites: reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz release_test: tutorials: + - *quickstart - path: elastic-tube-1d case_combination: - fluid-cpp @@ -142,3 +197,9 @@ test_suites: - solid-upstream-dealii - solid-downstream-dealii reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz + - *heat_exchanger_simplified + - path: free-flow-over-porous-media + case_combination: + - free-flow-dumux + - porous-media-dumux + reference_result: ./free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz From d55e7eaf82a96d98197a1e946fd9c87e9b796d45 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 12 May 2026 19:36:17 +0200 Subject: [PATCH 3/6] Rename environment variable --- changelog-entries/735.md | 2 +- tools/tests/README.md | 2 +- tools/tests/systemtests/Systemtest.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog-entries/735.md b/changelog-entries/735.md index e86208f00..56ca71a67 100644 --- a/changelog-entries/735.md +++ b/changelog-entries/735.md @@ -1 +1 @@ -- Add optional `timeout` field to `tests.yaml` entries for per-test timeout configuration. Overridable via `PRECICE_SYSTEMTEST_TIMEOUT` environment variable. +- Add optional `timeout` field to `tests.yaml` entries for per-test timeout configuration. Overridable via `PRECICE_SYSTEMTESTS_TIMEOUT` environment variable. diff --git a/tools/tests/README.md b/tools/tests/README.md index a841d3968..21facbabb 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -334,7 +334,7 @@ test_suites: timeout: 1200 ``` -The optional `timeout` field (in seconds) sets the maximum time for the solver run and fieldcompare phases of that specific case. If omitted, it defaults to `GLOBAL_TIMEOUT` (currently 900s, overridable via the `PRECICE_SYSTEMTEST_TIMEOUT` environment variable). +The optional `timeout` field (in seconds) sets the maximum time for the solver run and fieldcompare phases of that specific case. If omitted, it defaults to `GLOBAL_TIMEOUT` (currently 900s, overridable via the `PRECICE_SYSTEMTESTS_TIMEOUT` environment variable). This defines two test suites, namely `openfoam_adapter_pr` and `openfoam_adapter_release`. Each of them defines which case combinations of which tutorials to run. diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index 552f95d3d..e8711e915 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -19,7 +19,7 @@ import os -GLOBAL_TIMEOUT = int(os.environ.get("PRECICE_SYSTEMTEST_TIMEOUT", 900)) +GLOBAL_TIMEOUT = int(os.environ.get("PRECICE_SYSTEMTESTS_TIMEOUT", 900)) SHORT_TIMEOUT = 10 From 22521986af16f46150968264b415724323b85d06 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 12 May 2026 21:55:41 +0200 Subject: [PATCH 4/6] Revert reference results update --- .../fluid-openfoam_solid-fenics.tar.gz | 4 ++-- .../fluid-openfoam_solid-nutils.tar.gz | 4 ++-- .../fluid-openfoam_solid-openfoam.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz index 8c94ab4b6..2f0da01fe 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12035b666916914b92c6d3bc80a7e6f370d091e10151e3ead88d2ffb0438cf10 -size 771341 +oid sha256:059d338dd1e93b3661b052665fa297a55668e7e81d385d409b12dcbbe0ac74ed +size 771178 diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz index b30e8ad47..c8484c169 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:841150e71ba93405445c2c74dc3b8c1528d3285b966ca1e08589b95edba7a608 -size 532571 +oid sha256:96d2748fcc27be2b8977b30192965ae5a736c679e3a2f26c2f6713348c2bc2f4 +size 532749 diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 3942c917a..9d84f0178 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20c0756073564f10c4b692f8571ba507979ee034ac2530e10dbacb7fab447aa1 -size 498038 +oid sha256:758a2fb73703b7a693a8b2b2994dab0ff52e89b0b1ce25c8389571f3397cce15 +size 497675 diff --git a/flow-over-heated-plate/reference-results/reference_results.metadata b/flow-over-heated-plate/reference-results/reference_results.metadata index 6907b76fc..bff70c8d8 100644 --- a/flow-over-heated-plate/reference-results/reference_results.metadata +++ b/flow-over-heated-plate/reference-results/reference_results.metadata @@ -11,9 +11,9 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-03-20 17:25:51 | 20c0756073564f10c4b692f8571ba507979ee034ac2530e10dbacb7fab447aa1 | -| fluid-openfoam_solid-fenics.tar.gz | 2026-03-20 17:25:51 | 12035b666916914b92c6d3bc80a7e6f370d091e10151e3ead88d2ffb0438cf10 | -| fluid-openfoam_solid-nutils.tar.gz | 2026-03-20 17:25:51 | 841150e71ba93405445c2c74dc3b8c1528d3285b966ca1e08589b95edba7a608 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-11 07:45:27 | 758a2fb73703b7a693a8b2b2994dab0ff52e89b0b1ce25c8389571f3397cce15 | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-11 07:45:27 | 059d338dd1e93b3661b052665fa297a55668e7e81d385d409b12dcbbe0ac74ed | +| fluid-openfoam_solid-nutils.tar.gz | 2026-05-11 07:45:27 | 96d2748fcc27be2b8977b30192965ae5a736c679e3a2f26c2f6713348c2bc2f4 | ## List of arguments used to generate the files @@ -25,7 +25,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | e27f5d4 | +| TUTORIALS_REF | b18b3ef | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -35,14 +35,14 @@ We also include some information on the machine used to generate them | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | | DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_VERSION | v0.8.0 | +| MICRO_MANAGER_REF | v0.8.0 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine ### uname -a -Linux precice-tests 5.15.0-168-generic #178-Ubuntu SMP Fri Jan 9 19:05:03 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu From dc86f563481ac17c8950719159a1fbedf8991716 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 12 May 2026 21:57:01 +0200 Subject: [PATCH 5/6] Remove unrelated change in Nutils requirements.txt --- flow-over-heated-plate/solid-nutils/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow-over-heated-plate/solid-nutils/requirements.txt b/flow-over-heated-plate/solid-nutils/requirements.txt index 78eb2a14a..7ff7b5aa1 100644 --- a/flow-over-heated-plate/solid-nutils/requirements.txt +++ b/flow-over-heated-plate/solid-nutils/requirements.txt @@ -1,4 +1,4 @@ setuptools # required by nutils -nutils==7 +nutils~=7.3 numpy >1, <2 pyprecice~=3.0 From 4521fec1080dae7b1612e578277a3c500c0088ed Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 12 May 2026 23:17:40 +0200 Subject: [PATCH 6/6] Ensure that timeout is an integer --- tools/tests/systemtests/TestSuite.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/tests/systemtests/TestSuite.py b/tools/tests/systemtests/TestSuite.py index ae91f9705..dff317ad9 100644 --- a/tools/tests/systemtests/TestSuite.py +++ b/tools/tests/systemtests/TestSuite.py @@ -83,7 +83,14 @@ def from_yaml(cls, path, parsed_tutorials: Tutorials): if mtw_raw is not None and (not isinstance(mtw_raw, int) or mtw_raw <= 0): raise ValueError(f"max_time_windows must be a positive integer, got {mtw_raw!r}") max_time_windows_of_tutorial[tutorial].append(mtw_raw) - timeouts_of_tutorial[tutorial].append(tutorial_case.get('timeout', None)) + + timeout_value = tutorial_case.get('timeout', None) + if timeout_value is not None and not isinstance(timeout_value, int): + raise TypeError( + f"Expected 'timeout' to be an integer or None, but got {type(timeout_value).__name__} " + f"(value: {timeout_value}) in tutorial '{tutorial}'." + ) + timeouts_of_tutorial[tutorial].append(timeout_value) else: raise Exception( f"Could not find the following cases {tutorial_case['case-combination']} in the current metadata of tutorial {tutorial.name}")