From 043fb276ca9995afa8702b51ca3d73b828a1cafb Mon Sep 17 00:00:00 2001 From: syntron Date: Sun, 8 Feb 2026 20:52:18 +0100 Subject: [PATCH 01/11] (E001) update tests (v4.x.x) [test_*] reorder imports [tests_ModelicaDoE*] fix pylint hint * use .items() [tests_*] use OMSessionABC.get_version() [test_ModelicaSystemCmd] use get_model_name() instead of access to private variable _model_name [test_ModelicaSystemOMC] read file using utf-8 encoding / linter fix [test_ModelicaSystemRunner] update test case * ModelicaSystemRunner & OMCPath * ModelicaSystemRunner & OMPathRunnerLocal * ModelicaSystemRunner & OMPathRunnerBash * ModelicaSystemRunner & OMPathRunnerBash using docker * ModelicaSystemRunner & OMPathRunnerBash using WSL (not tested!) [test_OMCPath] update test case * OMCPath & OMCSessionZMQ * OMCPath & OMCSessionLocal * OMCPath & OMCSessionDocker * OMCPath & OMCSessionWSL (not tested!) * OMPathLocal & OMCSessionRunner * OMPathBash & OMCSessionRunner * OMPathBash & OMCSessionRunner in docker * OMPathBash & OMCSessionRunner in WSL (not tested!) add workflow to run unittests in ./tests [test_OMParser] use only the public interface => om_parser_basic() [test_OMTypedParser] rename file / use om_parser_typed() update tests - do NOT run test_FMIRegression.py reason: * it is only a test for OMC / not OMPython specific * furthermore, it is run automatically via cron job (= FMITest) [test_ModelExecutionCmd] rename from test_ModelicaSystemCmd --- OMPython/__init__.py | 2 + tests/test_FMIExport.py | 2 +- ...SystemCmd.py => test_ModelExecutionCmd.py} | 2 +- tests/test_ModelicaDoEOMC.py | 6 +- tests/test_ModelicaDoERunner.py | 6 +- tests/test_ModelicaSystemOMC.py | 2 +- tests/test_ModelicaSystemRunner.py | 176 +++++++++++++++++- tests/test_OMCPath.py | 96 +++++++--- tests/test_OMParser.py | 53 ++++-- tests/test_OMTypedParser.py | 65 +++++++ tests/test_ZMQ.py | 1 + tests/test_docker.py | 2 + tests/test_typedParser.py | 53 ------ 13 files changed, 365 insertions(+), 101 deletions(-) rename tests/{test_ModelicaSystemCmd.py => test_ModelExecutionCmd.py} (97%) create mode 100644 tests/test_OMTypedParser.py delete mode 100644 tests/test_typedParser.py diff --git a/OMPython/__init__.py b/OMPython/__init__.py index c12f8524..22c88137 100644 --- a/OMPython/__init__.py +++ b/OMPython/__init__.py @@ -30,6 +30,7 @@ OMPathABC, OMCPath, + OMSessionABC, OMSessionRunner, OMCSessionABC, @@ -77,6 +78,7 @@ 'OMPathABC', 'OMCPath', + 'OMSessionABC', 'OMSessionRunner', 'OMCSessionABC', diff --git a/tests/test_FMIExport.py b/tests/test_FMIExport.py index c7ab038a..65ac2766 100644 --- a/tests/test_FMIExport.py +++ b/tests/test_FMIExport.py @@ -1,6 +1,6 @@ -import shutil import os import pathlib +import shutil import OMPython diff --git a/tests/test_ModelicaSystemCmd.py b/tests/test_ModelExecutionCmd.py similarity index 97% rename from tests/test_ModelicaSystemCmd.py rename to tests/test_ModelExecutionCmd.py index 3d35376b..db5aadeb 100644 --- a/tests/test_ModelicaSystemCmd.py +++ b/tests/test_ModelExecutionCmd.py @@ -29,7 +29,7 @@ def mscmd_firstorder(model_firstorder): cmd_local=mod.get_session().model_execution_local, cmd_windows=mod.get_session().model_execution_windows, cmd_prefix=mod.get_session().model_execution_prefix(cwd=mod.getWorkDirectory()), - model_name=mod._model_name, + model_name=mod.get_model_name(), ) return mscmd diff --git a/tests/test_ModelicaDoEOMC.py b/tests/test_ModelicaDoEOMC.py index 143932fc..9d6afc63 100644 --- a/tests/test_ModelicaDoEOMC.py +++ b/tests/test_ModelicaDoEOMC.py @@ -159,6 +159,6 @@ def _run_ModelicaDoEOMC(doe_mod): f"y[{row['p']}]": float(row['b']), } - for var in var_dict: - assert var in sol['data'] - assert np.isclose(sol['data'][var][-1], var_dict[var]) + for key, val in var_dict.items(): + assert key in sol['data'] + assert np.isclose(sol['data'][key][-1], val) diff --git a/tests/test_ModelicaDoERunner.py b/tests/test_ModelicaDoERunner.py index 2d41315f..e29e7e05 100644 --- a/tests/test_ModelicaDoERunner.py +++ b/tests/test_ModelicaDoERunner.py @@ -153,6 +153,6 @@ def _check_runner_result(mod, doe_mod): 'b': float(row['b']), } - for var in var_dict: - assert var in sol['data'] - assert np.isclose(sol['data'][var][-1], var_dict[var]) + for key, val in var_dict.items(): + assert key in sol['data'] + assert np.isclose(sol['data'][key][-1], val) diff --git a/tests/test_ModelicaSystemOMC.py b/tests/test_ModelicaSystemOMC.py index 8dd17ef0..c63b92e1 100644 --- a/tests/test_ModelicaSystemOMC.py +++ b/tests/test_ModelicaSystemOMC.py @@ -495,7 +495,7 @@ def test_simulate_inputs(tmp_path): } mod.setInputs(**inputs) csv_file = mod._createCSVData() - assert pathlib.Path(csv_file).read_text() == """time,u1,u2,end + assert pathlib.Path(csv_file).read_text(encoding='utf-8') == """time,u1,u2,end 0.0,0.0,0.0,0 0.25,0.25,0.5,0 0.5,0.5,1.0,0 diff --git a/tests/test_ModelicaSystemRunner.py b/tests/test_ModelicaSystemRunner.py index ec9d734d..a207368c 100644 --- a/tests/test_ModelicaSystemRunner.py +++ b/tests/test_ModelicaSystemRunner.py @@ -1,9 +1,22 @@ +import sys + import numpy as np import pytest import OMPython +skip_on_windows = pytest.mark.skipif( + sys.platform.startswith("win"), + reason="OpenModelica Docker image is Linux-only; skipping on Windows.", +) + +skip_python_older_312 = pytest.mark.skipif( + sys.version_info < (3, 12), + reason="OMCPath(non-local) only working for Python >= 3.12.", +) + + @pytest.fixture def model_firstorder_content(): return """ @@ -37,7 +50,7 @@ def param(): } -def test_runner(model_firstorder, param): +def test_ModelicaSystemRunner_OMC(model_firstorder, param): # create a model using ModelicaSystem mod = OMPython.ModelicaSystemOMC() mod.model( @@ -71,6 +84,167 @@ def test_runner(model_firstorder, param): _check_result(mod=mod, resultfile=resultfile_modr, param=param) +def test_ModelicaSystemRunner_local(model_firstorder, param): + # create a model using ModelicaSystem + mod = OMPython.ModelicaSystemOMC() + mod.model( + model_file=model_firstorder, + model_name="M", + ) + + resultfile_mod = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_mod.mat" + _run_simulation(mod=mod, resultfile=resultfile_mod, param=param) + + # run the model using only the runner class + omcs = OMPython.OMSessionRunner( + version=mod.get_session().get_version(), + ompath_runner=OMPython.OMPathRunnerLocal, + ) + modr = OMPython.ModelicaSystemRunner( + session=omcs, + work_directory=mod.getWorkDirectory(), + ) + modr.setup( + model_name="M", + ) + + resultfile_modr = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_modr.mat" + _run_simulation(mod=modr, resultfile=resultfile_modr, param=param) + + # cannot check the content as runner does not have the capability to open a result file + assert resultfile_mod.size() == resultfile_modr.size() + + # check results + _check_result(mod=mod, resultfile=resultfile_mod, param=param) + _check_result(mod=mod, resultfile=resultfile_modr, param=param) + + +@skip_on_windows +def test_ModelicaSystemRunner_bash(model_firstorder, param): + # create a model using ModelicaSystem + mod = OMPython.ModelicaSystemOMC() + mod.model( + model_file=model_firstorder, + model_name="M", + ) + + resultfile_mod = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_mod.mat" + _run_simulation(mod=mod, resultfile=resultfile_mod, param=param) + + # run the model using only the runner class + omcsr = OMPython.OMSessionRunner( + version=mod.get_session().get_version(), + ompath_runner=OMPython.OMPathRunnerBash, + ) + modr = OMPython.ModelicaSystemRunner( + session=omcsr, + work_directory=mod.getWorkDirectory(), + ) + modr.setup( + model_name="M", + ) + + resultfile_modr = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_modr.mat" + _run_simulation(mod=modr, resultfile=resultfile_modr, param=param) + + # cannot check the content as runner does not have the capability to open a result file + assert resultfile_mod.size() == resultfile_modr.size() + + # check results + _check_result(mod=mod, resultfile=resultfile_mod, param=param) + _check_result(mod=mod, resultfile=resultfile_modr, param=param) + + +@skip_on_windows +@skip_python_older_312 +def test_ModelicaSystemRunner_bash_docker(model_firstorder, param): + omcs = OMPython.OMCSessionDocker(docker="openmodelica/openmodelica:v1.25.0-minimal") + omversion = omcs.sendExpression("getVersion()") + assert isinstance(omversion, str) and omversion.startswith("OpenModelica") + + # create a model using ModelicaSystem + mod = OMPython.ModelicaSystemOMC( + session=omcs, + ) + mod.model( + model_file=model_firstorder, + model_name="M", + ) + + resultfile_mod = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_mod.mat" + _run_simulation(mod=mod, resultfile=resultfile_mod, param=param) + + # run the model using only the runner class + omcsr = OMPython.OMSessionRunner( + version=mod.get_session().get_version(), + cmd_prefix=omcs.model_execution_prefix(cwd=mod.getWorkDirectory()), + ompath_runner=OMPython.OMPathRunnerBash, + model_execution_local=False, + ) + modr = OMPython.ModelicaSystemRunner( + session=omcsr, + work_directory=mod.getWorkDirectory(), + ) + modr.setup( + model_name="M", + ) + + resultfile_modr = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_modr.mat" + _run_simulation(mod=modr, resultfile=resultfile_modr, param=param) + + # cannot check the content as runner does not have the capability to open a result file + assert resultfile_mod.size() == resultfile_modr.size() + + # check results + _check_result(mod=mod, resultfile=resultfile_mod, param=param) + _check_result(mod=mod, resultfile=resultfile_modr, param=param) + + +@pytest.mark.skip(reason="Not able to run WSL on github") +@skip_python_older_312 +def test_ModelicaSystemDoE_WSL(tmp_path, model_doe, param_doe): + omcs = OMPython.OMCSessionWSL() + omversion = omcs.sendExpression("getVersion()") + assert isinstance(omversion, str) and omversion.startswith("OpenModelica") + + # create a model using ModelicaSystem + mod = OMPython.ModelicaSystemOMC( + session=omcs, + ) + mod.model( + model_file=model_firstorder, + model_name="M", + ) + + resultfile_mod = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_mod.mat" + _run_simulation(mod=mod, resultfile=resultfile_mod, param=param) + + # run the model using only the runner class + omcsr = OMPython.OMSessionRunner( + version=mod.get_session().get_version(), + cmd_prefix=omcs.model_execution_prefix(cwd=mod.getWorkDirectory()), + ompath_runner=OMPython.OMPathRunnerBash, + model_execution_local=False, + ) + modr = OMPython.ModelicaSystemRunner( + session=omcsr, + work_directory=mod.getWorkDirectory(), + ) + modr.setup( + model_name="M", + ) + + resultfile_modr = mod.getWorkDirectory() / f"{mod.get_model_name()}_res_modr.mat" + _run_simulation(mod=modr, resultfile=resultfile_modr, param=param) + + # cannot check the content as runner does not have the capability to open a result file + assert resultfile_mod.size() == resultfile_modr.size() + + # check results + _check_result(mod=mod, resultfile=resultfile_mod, param=param) + _check_result(mod=mod, resultfile=resultfile_modr, param=param) + + def _run_simulation(mod, resultfile, param): simOptions = {"stopTime": param['stopTime'], "stepSize": 0.1, "tolerance": 1e-8} mod.setSimulationOptions(**simOptions) diff --git a/tests/test_OMCPath.py b/tests/test_OMCPath.py index df01b86a..e15c75ff 100644 --- a/tests/test_OMCPath.py +++ b/tests/test_OMCPath.py @@ -15,42 +15,98 @@ ) -def test_OMCPath_OMCProcessLocal(): - omcs = OMPython.OMCSessionLocal() +# TODO: based on compatibility layer +def test_OMCPath_OMCSessionZMQ(): + om = OMPython.OMCSessionZMQ() - _run_OMCPath_checks(omcs) + _run_OMPath_checks(om) + _run_OMPath_write_file(om) - del omcs + +def test_OMCPath_OMCSessionLocal(): + oms = OMPython.OMCSessionLocal() + + _run_OMPath_checks(oms) + _run_OMPath_write_file(oms) @skip_on_windows @skip_python_older_312 -def test_OMCPath_OMCProcessDocker(): +def test_OMCPath_OMCSessionDocker(): omcs = OMPython.OMCSessionDocker(docker="openmodelica/openmodelica:v1.25.0-minimal") omversion = omcs.sendExpression("getVersion()") assert isinstance(omversion, str) and omversion.startswith("OpenModelica") - _run_OMCPath_checks(omcs) - - del omcs + _run_OMPath_checks(omcs) + _run_OMPath_write_file(omcs) @pytest.mark.skip(reason="Not able to run WSL on github") @skip_python_older_312 -def test_OMCPath_OMCProcessWSL(): - omcs = OMPython.OMCSessionWSL( +def test_OMCPath_OMCSessionWSL(): + oms = OMPython.OMCSessionWSL( wsl_omc='omc', wsl_user='omc', timeout=30.0, ) - _run_OMCPath_checks(omcs) + _run_OMPath_checks(oms) + _run_OMPath_write_file(oms) + + +@skip_python_older_312 +def test_OMPathLocal_OMSessionRunner(): + oms = OMPython.OMSessionRunner() + + _run_OMPath_checks(oms) + _run_OMPath_write_file(oms) + + +@skip_on_windows +@skip_python_older_312 +def test_OMPathBash_OMSessionRunner(): + oms = OMPython.OMSessionRunner( + ompath_runner=OMPython.OMPathRunnerBash, + ) + + _run_OMPath_checks(oms) + _run_OMPath_write_file(oms) + + +@skip_on_windows +@skip_python_older_312 +def test_OMPathBash_OMSessionRunner_Docker(): + oms_docker = OMPython.OMCSessionDocker(docker="openmodelica/openmodelica:v1.25.0-minimal") + omversion = oms_docker.sendExpression("getVersion()") + assert isinstance(omversion, str) and omversion.startswith("OpenModelica") + + oms = OMPython.OMSessionRunner( + cmd_prefix=oms_docker.get_cmd_prefix(), + ompath_runner=OMPython.OMPathRunnerBash, + ) + + _run_OMPath_checks(oms) + _run_OMPath_write_file(oms) - del omcs +@pytest.mark.skip(reason="Not able to run WSL on github") +@skip_python_older_312 +def test_OMPathBash_OMSessionRunner_WSL(): + oms_docker = OMPython.OMCSessionWSL() + omversion = oms_docker.sendExpression("getVersion()") + assert isinstance(omversion, str) and omversion.startswith("OpenModelica") + + oms = OMPython.OMSessionRunner( + cmd_prefix=oms_docker.get_cmd_prefix(), + ompath_runner=OMPython.OMPathRunnerBash, + ) + + _run_OMPath_checks(oms) + _run_OMPath_write_file(oms) -def _run_OMCPath_checks(omcs: OMPython.OMCSessionABC): - p1 = omcs.omcpath_tempdir() + +def _run_OMPath_checks(om: OMPython.OMSessionABC): + p1 = om.omcpath_tempdir() p2 = p1 / 'test' p2.mkdir() assert p2.is_dir() @@ -59,8 +115,8 @@ def _run_OMCPath_checks(omcs: OMPython.OMCSessionABC): assert p3.write_text('test') assert p3.is_file() assert p3.size() > 0 - p3 = p3.resolve().absolute() - assert str(p3) == str((p2 / 'test.txt').resolve().absolute()) + p3 = p3.resolve() + assert str(p3) == str((p2 / 'test.txt').resolve()) assert p3.read_text() == "test" assert p3.is_file() assert p3.parent.is_dir() @@ -68,15 +124,11 @@ def _run_OMCPath_checks(omcs: OMPython.OMCSessionABC): assert p3.is_file() is False -def test_OMCPath_write_file(tmpdir): - omcs = OMPython.OMCSessionLocal() - +def _run_OMPath_write_file(om: OMPython.OMSessionABC): data = "abc # \\t # \" # \\n # xyz" - p1 = omcs.omcpath_tempdir() + p1 = om.omcpath_tempdir() p2 = p1 / 'test.txt' p2.write_text(data=data) assert data == p2.read_text() - - del omcs diff --git a/tests/test_OMParser.py b/tests/test_OMParser.py index 875604e5..9dca784d 100644 --- a/tests/test_OMParser.py +++ b/tests/test_OMParser.py @@ -1,6 +1,6 @@ -from OMPython import OMParser +import OMPython -typeCheck = OMParser.typeCheck +parser = OMPython.OMParser.om_parser_basic def test_newline_behaviour(): @@ -8,31 +8,38 @@ def test_newline_behaviour(): def test_boolean(): - assert typeCheck('TRUE') is True - assert typeCheck('True') is True - assert typeCheck('true') is True - assert typeCheck('FALSE') is False - assert typeCheck('False') is False - assert typeCheck('false') is False + assert parser('TRUE') is True + assert parser('True') is True + assert parser('true') is True + assert parser('FALSE') is False + assert parser('False') is False + assert parser('false') is False def test_int(): - assert typeCheck('2') == 2 - assert type(typeCheck('1')) == int - assert type(typeCheck('123123123123123123232323')) == int - assert type(typeCheck('9223372036854775808')) == int + assert parser('2') == 2 + assert type(parser('1')) == int + assert type(parser('123123123123123123232323')) == int + assert type(parser('9223372036854775808')) == int def test_float(): - assert type(typeCheck('1.2e3')) == float + assert type(parser('1.2e3')) == float -# def test_dict(): -# assert type(typeCheck('{"a": "b"}')) == dict +def test_dict(): + # TODO: why does it fail? + # assert type(parser('{"a": "b"}')) == dict + pass def test_ident(): - assert typeCheck('blabla2') == "blabla2" + assert parser('blabla2') == "blabla2" + + +def test_empty(): + # TODO: this differs from OMTypedParser + assert parser('') == {} def test_str(): @@ -41,3 +48,17 @@ def test_str(): def test_UnStringable(): pass + + +# def test_everything(): +# # this test used to be in OMTypedParser.py's main() +# testdata = """ +# (1.0,{{1,true,3},{"4\\" +# ",5.9,6,NONE ( )},record ABC +# startTime = ErrorLevel.warning, +# 'stop*Time' = SOME(1.0) +# end ABC;}) +# """ +# expected = (1.0, ((1, True, 3), ('4"\n', 5.9, 6, None), {"'stop*Time'": 1.0, 'startTime': 'ErrorLevel.warning'})) +# results = parser(testdata) +# assert results == expected diff --git a/tests/test_OMTypedParser.py b/tests/test_OMTypedParser.py new file mode 100644 index 00000000..94a14210 --- /dev/null +++ b/tests/test_OMTypedParser.py @@ -0,0 +1,65 @@ +import OMPython + +parser = OMPython.OMTypedParser.om_parser_typed + + +def test_newline_behaviour(): + pass + + +def test_boolean(): + # TODO: why does these fail? + # assert parser('TRUE') is True + # assert parser('True') is True + assert parser('true') is True + # TODO: why does these fail? + # assert parser('FALSE') is False + # assert parser('False') is False + assert parser('false') is False + + +def test_int(): + assert parser('2') == 2 + assert type(parser('1')) == int + assert type(parser('123123123123123123232323')) == int + assert type(parser('9223372036854775808')) == int + + +def test_float(): + assert type(parser('1.2e3')) == float + + +def test_dict(): + # TODO: why does it fail? + # assert type(parser('{"a": "b"}')) == dict + pass + + +def test_ident(): + assert parser('blabla2') == "blabla2" + + +def test_empty(): + assert parser('') is None + + +def test_str(): + pass + + +def test_UnStringable(): + pass + + +def test_everything(): + # this test used to be in OMTypedParser.py's main() + testdata = """ + (1.0,{{1,true,3},{"4\\" +",5.9,6,NONE ( )},record ABC + startTime = ErrorLevel.warning, + 'stop*Time' = SOME(1.0) +end ABC;}) + """ + expected = (1.0, ((1, True, 3), ('4"\n', 5.9, 6, None), {"'stop*Time'": 1.0, 'startTime': 'ErrorLevel.warning'})) + results = parser(testdata) + assert results == expected diff --git a/tests/test_ZMQ.py b/tests/test_ZMQ.py index 1302a79d..89a8387b 100644 --- a/tests/test_ZMQ.py +++ b/tests/test_ZMQ.py @@ -1,5 +1,6 @@ import pathlib import os + import pytest import OMPython diff --git a/tests/test_docker.py b/tests/test_docker.py index a1acfbe1..50d2763a 100644 --- a/tests/test_docker.py +++ b/tests/test_docker.py @@ -1,5 +1,7 @@ import sys + import pytest + import OMPython skip_on_windows = pytest.mark.skipif( diff --git a/tests/test_typedParser.py b/tests/test_typedParser.py deleted file mode 100644 index 8e74a556..00000000 --- a/tests/test_typedParser.py +++ /dev/null @@ -1,53 +0,0 @@ -from OMPython import OMTypedParser - -typeCheck = OMTypedParser.om_parser_typed - - -def test_newline_behaviour(): - pass - - -def test_boolean(): - assert typeCheck('true') is True - assert typeCheck('false') is False - - -def test_int(): - assert typeCheck('2') == 2 - assert type(typeCheck('1')) == int - assert type(typeCheck('123123123123123123232323')) == int - assert type(typeCheck('9223372036854775808')) == int - - -def test_float(): - assert type(typeCheck('1.2e3')) == float - - -def test_ident(): - assert typeCheck('blabla2') == "blabla2" - - -def test_empty(): - assert typeCheck('') is None - - -def test_str(): - pass - - -def test_UnStringable(): - pass - - -def test_everything(): - # this test used to be in OMTypedParser.py's main() - testdata = """ - (1.0,{{1,true,3},{"4\\" -",5.9,6,NONE ( )},record ABC - startTime = ErrorLevel.warning, - 'stop*Time' = SOME(1.0) -end ABC;}) - """ - expected = (1.0, ((1, True, 3), ('4"\n', 5.9, 6, None), {"'stop*Time'": 1.0, 'startTime': 'ErrorLevel.warning'})) - results = typeCheck(testdata) - assert results == expected From 72a18da39613a50ee2182f36bd87f0348d1a84d9 Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 20 May 2026 20:59:50 +0200 Subject: [PATCH 02/11] update timeout to 30s to keep windows happy * windows unittests on github need a longer time to run the tests --- OMPython/ModelicaSystem.py | 4 ++-- OMPython/OMCSession.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 01e5bfbd..d571ab08 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -108,7 +108,7 @@ def __init__( cmd_prefix: list[str], cmd_local: bool = False, cmd_windows: bool = False, - timeout: float = 10.0, + timeout: float = 30.0, model_name: Optional[str] = None, ) -> None: if model_name is None: @@ -2835,7 +2835,7 @@ def __init__( self, runpath: pathlib.Path, modelname: str, - timeout: float = 10.0, + timeout: float = 30.0, ) -> None: super().__init__( runpath=runpath, diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index 04b5d9cc..e619e870 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -940,7 +940,7 @@ def __init__( self.model_execution_local = False # store variables - self._timeout = 10.0 + self._timeout = 30.0 self.set_timeout(timeout=timeout) # command prefix (to be used for docker or WSL) self._cmd_prefix: list[str] = [] From a5ff0a1fa2e19daee1757036f7bb450f0769f555 Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 20 May 2026 22:03:17 +0200 Subject: [PATCH 03/11] increase timeout to 60s --- OMPython/ModelicaSystem.py | 4 ++-- OMPython/OMCSession.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index d571ab08..9030eeb3 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -108,7 +108,7 @@ def __init__( cmd_prefix: list[str], cmd_local: bool = False, cmd_windows: bool = False, - timeout: float = 30.0, + timeout: float = 60.0, model_name: Optional[str] = None, ) -> None: if model_name is None: @@ -2835,7 +2835,7 @@ def __init__( self, runpath: pathlib.Path, modelname: str, - timeout: float = 30.0, + timeout: float = 60.0, ) -> None: super().__init__( runpath=runpath, diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index e619e870..920d8b94 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -940,7 +940,7 @@ def __init__( self.model_execution_local = False # store variables - self._timeout = 30.0 + self._timeout = 60.0 self.set_timeout(timeout=timeout) # command prefix (to be used for docker or WSL) self._cmd_prefix: list[str] = [] From 1b7eec47b01b61c3d637297296420ad07e978ad7 Mon Sep 17 00:00:00 2001 From: Adeel Asghar Date: Thu, 21 May 2026 14:28:47 +0200 Subject: [PATCH 04/11] Use 5 minutes as default timeout --- OMPython/ModelicaSystem.py | 4 ++-- OMPython/OMCSession.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 9030eeb3..ccf31adf 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -108,7 +108,7 @@ def __init__( cmd_prefix: list[str], cmd_local: bool = False, cmd_windows: bool = False, - timeout: float = 60.0, + timeout: float = 300.0, model_name: Optional[str] = None, ) -> None: if model_name is None: @@ -2835,7 +2835,7 @@ def __init__( self, runpath: pathlib.Path, modelname: str, - timeout: float = 60.0, + timeout: float = 300.0, ) -> None: super().__init__( runpath=runpath, diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index 920d8b94..3130baee 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -940,7 +940,7 @@ def __init__( self.model_execution_local = False # store variables - self._timeout = 60.0 + self._timeout = 300.0 self.set_timeout(timeout=timeout) # command prefix (to be used for docker or WSL) self._cmd_prefix: list[str] = [] From 6d9348640af6fa758f071e66d036d295274e290c Mon Sep 17 00:00:00 2001 From: Adeel Asghar Date: Fri, 22 May 2026 12:51:04 +0200 Subject: [PATCH 05/11] Debug error --- .github/workflows/Test.yml | 12 +++++++++--- OMPython/ModelicaSystem.py | 1 + OMPython/OMCSession.py | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index c4f9e6e8..73e68b95 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -17,13 +17,13 @@ jobs: # test for: # * oldest supported version # * latest available Python version - python-version: ['3.10', '3.14'] + python-version: ['3.10'] # * Linux using ubuntu-latest # * Windows using windows-latest - os: ['ubuntu-latest', 'windows-latest'] + os: ['windows-latest'] # * OM stable - latest stable version # * OM nightly - latest nightly build - omc-version: ['stable', 'nightly'] + omc-version: ['stable'] steps: - uses: actions/checkout@v6 @@ -67,6 +67,12 @@ jobs: - name: Check twine run: python -m twine check dist/* + - name: Disable Windows Error Reporting dialogs + if: runner.os == 'Windows' + run: | + reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v DontShowUI /t REG_DWORD /d 1 /f + reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v Disabled /t REG_DWORD /d 1 /f + - name: Run pytest uses: pavelzw/pytest-action@v2 with: diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index ccf31adf..4de1b4a6 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -272,6 +272,7 @@ def definition(self) -> ModelExecutionData: match = re.match(pattern=r"^SET PATH=([^%]*)", string=line, flags=re.IGNORECASE) if match: cmd_library_path = match.group(1).strip(';') # Remove any trailing semicolons + logger.debug(f"ModelExecutionData::definition Set library path for model executable: {repr(cmd_library_path)}") my_env = os.environ.copy() my_env["PATH"] = cmd_library_path + os.pathsep + my_env["PATH"] diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index 3130baee..aab7b098 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -851,6 +851,7 @@ def run(self) -> int: """ my_env = os.environ.copy() + logger.debug(f"ModelExecutionData::run() - Set library path for model executable: {repr(self.cmd_library_path)}") if isinstance(self.cmd_library_path, str): my_env["PATH"] = self.cmd_library_path + os.pathsep + my_env["PATH"] From 5260d95e3bd6799eb420e299bf2d61b979a237a5 Mon Sep 17 00:00:00 2001 From: Adeel Asghar Date: Fri, 22 May 2026 12:56:51 +0200 Subject: [PATCH 06/11] Linter --- OMPython/ModelicaSystem.py | 2 +- OMPython/OMCSession.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 4de1b4a6..44343d27 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -272,7 +272,7 @@ def definition(self) -> ModelExecutionData: match = re.match(pattern=r"^SET PATH=([^%]*)", string=line, flags=re.IGNORECASE) if match: cmd_library_path = match.group(1).strip(';') # Remove any trailing semicolons - logger.debug(f"ModelExecutionData::definition Set library path for model executable: {repr(cmd_library_path)}") + logger.debug(f"ModelExecutionData::definition cmd_library_path: {repr(cmd_library_path)}") my_env = os.environ.copy() my_env["PATH"] = cmd_library_path + os.pathsep + my_env["PATH"] diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index aab7b098..6216d281 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -851,7 +851,7 @@ def run(self) -> int: """ my_env = os.environ.copy() - logger.debug(f"ModelExecutionData::run() - Set library path for model executable: {repr(self.cmd_library_path)}") + logger.debug(f"ModelExecutionData::run() self.cmd_library_path: {repr(self.cmd_library_path)}") if isinstance(self.cmd_library_path, str): my_env["PATH"] = self.cmd_library_path + os.pathsep + my_env["PATH"] From 4a49fc8104f3a21ebd47072628df9d09a0ddb75a Mon Sep 17 00:00:00 2001 From: Adeel Asghar Date: Fri, 22 May 2026 13:33:46 +0200 Subject: [PATCH 07/11] Run with DEBUG --- .github/workflows/Test.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 73e68b95..e067bafb 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -7,6 +7,11 @@ on: - 'v*' # only publish when pushing version tags (e.g., v1.0.0) pull_request: workflow_dispatch: + inputs: + pytest_args: + description: 'Extra pytest arguments' + required: false + default: '' jobs: test: @@ -79,19 +84,19 @@ jobs: verbose: true emoji: true job-summary: true - custom-arguments: '-v ./tests' + custom-arguments: '-v --log-cli-level=DEBUG ${{ inputs.pytest_args }} ./tests/test_linearization.py' click-to-expand: true report-title: 'Test Report' - - name: Run pytest based on v4.0.0 compatibility layer - uses: pavelzw/pytest-action@v2 - with: - verbose: true - emoji: true - job-summary: true - custom-arguments: '-v ./tests_v400' - click-to-expand: true - report-title: 'Test Report (v4.0.0 compatibility layer)' + # - name: Run pytest based on v4.0.0 compatibility layer + # uses: pavelzw/pytest-action@v2 + # with: + # verbose: true + # emoji: true + # job-summary: true + # custom-arguments: '-v ./tests_v400' + # click-to-expand: true + # report-title: 'Test Report (v4.0.0 compatibility layer)' Publish: name: Publish to PyPI From d22e0bb80e0fbe24e17e78924f1e66d45ad63b66 Mon Sep 17 00:00:00 2001 From: Adeel Asghar Date: Fri, 22 May 2026 13:53:04 +0200 Subject: [PATCH 08/11] Run all tests --- .github/workflows/Test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index e067bafb..5c6f475b 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -84,7 +84,7 @@ jobs: verbose: true emoji: true job-summary: true - custom-arguments: '-v --log-cli-level=DEBUG ${{ inputs.pytest_args }} ./tests/test_linearization.py' + custom-arguments: '-v --log-cli-level=DEBUG ${{ inputs.pytest_args }} ./tests' click-to-expand: true report-title: 'Test Report' From 63357b33ffe56ff8a3c8ed9d9b4e3d428ea0ddae Mon Sep 17 00:00:00 2001 From: Adeel Asghar Date: Fri, 22 May 2026 14:27:10 +0200 Subject: [PATCH 09/11] More testing --- .github/workflows/Test.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 5c6f475b..d03b4f82 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -22,13 +22,13 @@ jobs: # test for: # * oldest supported version # * latest available Python version - python-version: ['3.10'] + python-version: ['3.10', '3.14'] # * Linux using ubuntu-latest # * Windows using windows-latest - os: ['windows-latest'] + os: ['ubuntu-latest', 'windows-latest'] # * OM stable - latest stable version # * OM nightly - latest nightly build - omc-version: ['stable'] + omc-version: ['stable', 'nightly'] steps: - uses: actions/checkout@v6 @@ -72,12 +72,6 @@ jobs: - name: Check twine run: python -m twine check dist/* - - name: Disable Windows Error Reporting dialogs - if: runner.os == 'Windows' - run: | - reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v DontShowUI /t REG_DWORD /d 1 /f - reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v Disabled /t REG_DWORD /d 1 /f - - name: Run pytest uses: pavelzw/pytest-action@v2 with: From 9f2d4500233cf2c652fa8063df038794cd0ebfe5 Mon Sep 17 00:00:00 2001 From: Adeel Asghar Date: Fri, 22 May 2026 15:20:53 +0200 Subject: [PATCH 10/11] Increase workflow timeout --- .github/workflows/Test.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index d03b4f82..d6e48a0e 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -16,7 +16,7 @@ on: jobs: test: runs-on: ${{ matrix.os }} - timeout-minutes: 45 + timeout-minutes: 60 strategy: matrix: # test for: @@ -78,19 +78,19 @@ jobs: verbose: true emoji: true job-summary: true - custom-arguments: '-v --log-cli-level=DEBUG ${{ inputs.pytest_args }} ./tests' + custom-arguments: '-v ${{ inputs.pytest_args }} ./tests' click-to-expand: true report-title: 'Test Report' - # - name: Run pytest based on v4.0.0 compatibility layer - # uses: pavelzw/pytest-action@v2 - # with: - # verbose: true - # emoji: true - # job-summary: true - # custom-arguments: '-v ./tests_v400' - # click-to-expand: true - # report-title: 'Test Report (v4.0.0 compatibility layer)' + - name: Run pytest based on v4.0.0 compatibility layer + uses: pavelzw/pytest-action@v2 + with: + verbose: true + emoji: true + job-summary: true + custom-arguments: '-v ${{ inputs.pytest_args }} ./tests_v400' + click-to-expand: true + report-title: 'Test Report (v4.0.0 compatibility layer)' Publish: name: Publish to PyPI From 064cedd7b7822055f73d0abe509f00f081c3bf0c Mon Sep 17 00:00:00 2001 From: Adeel Asghar Date: Fri, 22 May 2026 21:13:15 +0200 Subject: [PATCH 11/11] Debug --- .github/workflows/Test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index d6e48a0e..5d8c3e75 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -78,7 +78,7 @@ jobs: verbose: true emoji: true job-summary: true - custom-arguments: '-v ${{ inputs.pytest_args }} ./tests' + custom-arguments: '-v --log-cli-level=DEBUG ${{ inputs.pytest_args }} ./tests' click-to-expand: true report-title: 'Test Report' @@ -88,7 +88,7 @@ jobs: verbose: true emoji: true job-summary: true - custom-arguments: '-v ${{ inputs.pytest_args }} ./tests_v400' + custom-arguments: '-v --log-cli-level=DEBUG ${{ inputs.pytest_args }} ./tests_v400' click-to-expand: true report-title: 'Test Report (v4.0.0 compatibility layer)'