1313import pytest
1414
1515from feelpp .mo2fmu import compileFmu
16+ from feelpp .mo2fmu .compilers .base import CompilationResult
1617from feelpp .mo2fmu .compilers .dymola import DymolaConfig
1718
1819
@@ -52,6 +53,41 @@ def _get_dymola_config() -> DymolaConfig:
5253 )
5354
5455
56+ def _hasNoShareableLicense (result : CompilationResult ) -> bool :
57+ """Return True when Dymola compilation fell back to the trial license."""
58+ errorMessage = (result .error_message or "" ).lower ()
59+ unavailablePatterns = (
60+ "shareable license is not available" ,
61+ "shareable license users exceeded" ,
62+ "maximum number of shareable license users exceeded" ,
63+ "trial license" ,
64+ )
65+ return any (pattern in errorMessage for pattern in unavailablePatterns )
66+
67+
68+ def _compileWithDymolaOrSkip (
69+ * ,
70+ mo : Path ,
71+ outdir : Path ,
72+ fmiType : str ,
73+ fmiVersion : str ,
74+ ) -> CompilationResult :
75+ """Compile with Dymola, skipping the test when no floating seat is available."""
76+ result = compileFmu (
77+ mo = mo ,
78+ outdir = outdir ,
79+ backend = "dymola" ,
80+ fmiType = fmiType ,
81+ fmiVersion = fmiVersion ,
82+ verbose = True ,
83+ force = True ,
84+ dymolaConfig = _get_dymola_config (),
85+ )
86+ if _hasNoShareableLicense (result ):
87+ pytest .skip (f"Dymola compile license not available:\n { result .error_message } " )
88+ return result
89+
90+
5591# =============================================================================
5692# FMPy Simulation Tests
5793# =============================================================================
@@ -69,15 +105,11 @@ def test_simulate_cosimulation_fmu(self, simpleOdeModel: Path, tmp_path: Path) -
69105
70106 # Compile the model to Co-Simulation FMU
71107 outdir = tmp_path / "output_cs"
72- result = compileFmu (
108+ result = _compileWithDymolaOrSkip (
73109 mo = simpleOdeModel ,
74110 outdir = outdir ,
75- backend = "dymola" ,
76111 fmiType = "cs" ,
77112 fmiVersion = "2" ,
78- verbose = True ,
79- force = True ,
80- dymolaConfig = _get_dymola_config (),
81113 )
82114
83115 assert result .success , f"Compilation failed: { result .error_message } "
@@ -109,15 +141,11 @@ def test_simulate_model_exchange_fmu(self, simpleOdeModel: Path, tmp_path: Path)
109141
110142 # Compile the model to Model Exchange FMU
111143 outdir = tmp_path / "output_me"
112- result = compileFmu (
144+ result = _compileWithDymolaOrSkip (
113145 mo = simpleOdeModel ,
114146 outdir = outdir ,
115- backend = "dymola" ,
116147 fmiType = "me" ,
117148 fmiVersion = "2" ,
118- verbose = True ,
119- force = True ,
120- dymolaConfig = _get_dymola_config (),
121149 )
122150
123151 assert result .success , f"Compilation failed: { result .error_message } "
@@ -159,15 +187,11 @@ def test_simulate_model_exchange_sinusoidal(
159187
160188 # Compile to Model Exchange
161189 outdir = tmp_path / "output_sin_me"
162- result = compileFmu (
190+ result = _compileWithDymolaOrSkip (
163191 mo = odeSinusoidalModel ,
164192 outdir = outdir ,
165- backend = "dymola" ,
166193 fmiType = "me" ,
167194 fmiVersion = "2" ,
168- verbose = True ,
169- force = True ,
170- dymolaConfig = _get_dymola_config (),
171195 )
172196
173197 assert result .success , f"Compilation failed: { result .error_message } "
@@ -198,29 +222,21 @@ def test_compare_cs_and_me_results(self, simpleOdeModel: Path, tmp_path: Path) -
198222
199223 # Compile Co-Simulation FMU
200224 outdir_cs = tmp_path / "output_cs"
201- result_cs = compileFmu (
225+ result_cs = _compileWithDymolaOrSkip (
202226 mo = simpleOdeModel ,
203227 outdir = outdir_cs ,
204- backend = "dymola" ,
205228 fmiType = "cs" ,
206229 fmiVersion = "2" ,
207- verbose = True ,
208- force = True ,
209- dymolaConfig = _get_dymola_config (),
210230 )
211231 assert result_cs .success
212232
213233 # Compile Model Exchange FMU
214234 outdir_me = tmp_path / "output_me"
215- result_me = compileFmu (
235+ result_me = _compileWithDymolaOrSkip (
216236 mo = simpleOdeModel ,
217237 outdir = outdir_me ,
218- backend = "dymola" ,
219238 fmiType = "me" ,
220239 fmiVersion = "2" ,
221- verbose = True ,
222- force = True ,
223- dymolaConfig = _get_dymola_config (),
224240 )
225241 assert result_me .success
226242
@@ -261,15 +277,11 @@ def test_simulate_fmi3_model_exchange(self, simpleOdeModel: Path, tmp_path: Path
261277
262278 # Compile to FMI 3.0 Model Exchange
263279 outdir = tmp_path / "output_fmi3_me"
264- result = compileFmu (
280+ result = _compileWithDymolaOrSkip (
265281 mo = simpleOdeModel ,
266282 outdir = outdir ,
267- backend = "dymola" ,
268283 fmiType = "me" ,
269284 fmiVersion = "3" ,
270- verbose = True ,
271- force = True ,
272- dymolaConfig = _get_dymola_config (),
273285 )
274286
275287 # FMI 3.0 may not be supported by all Dymola versions
@@ -301,15 +313,11 @@ def test_simulate_fmi3_cosimulation(self, simpleOdeModel: Path, tmp_path: Path)
301313
302314 # Compile to FMI 3.0 Co-Simulation
303315 outdir = tmp_path / "output_fmi3_cs"
304- result = compileFmu (
316+ result = _compileWithDymolaOrSkip (
305317 mo = simpleOdeModel ,
306318 outdir = outdir ,
307- backend = "dymola" ,
308319 fmiType = "cs" ,
309320 fmiVersion = "3" ,
310- verbose = True ,
311- force = True ,
312- dymolaConfig = _get_dymola_config (),
313321 )
314322
315323 # FMI 3.0 may not be supported
@@ -344,15 +352,11 @@ def test_validate_fmu(self, simpleOdeModel: Path, tmp_path: Path) -> None:
344352
345353 # Compile the model
346354 outdir = tmp_path / "output"
347- result = compileFmu (
355+ result = _compileWithDymolaOrSkip (
348356 mo = simpleOdeModel ,
349357 outdir = outdir ,
350- backend = "dymola" ,
351358 fmiType = "cs" ,
352359 fmiVersion = "2" ,
353- verbose = True ,
354- force = True ,
355- dymolaConfig = _get_dymola_config (),
356360 )
357361
358362 assert result .success
@@ -377,15 +381,11 @@ def test_read_model_variables(self, simpleOdeModel: Path, tmp_path: Path) -> Non
377381
378382 # Compile the model
379383 outdir = tmp_path / "output"
380- result = compileFmu (
384+ result = _compileWithDymolaOrSkip (
381385 mo = simpleOdeModel ,
382386 outdir = outdir ,
383- backend = "dymola" ,
384387 fmiType = "me" ,
385388 fmiVersion = "2" ,
386- verbose = True ,
387- force = True ,
388- dymolaConfig = _get_dymola_config (),
389389 )
390390
391391 assert result .success
@@ -420,15 +420,11 @@ class TestBouncingBallSimulation:
420420 def test_compile_bouncing_ball_fmi2 (self , bouncingBallModel : Path , tmp_path : Path ) -> None :
421421 """Test compiling bouncing ball to FMI 2.0."""
422422 outdir = tmp_path / "output_bb_fmi2"
423- result = compileFmu (
423+ result = _compileWithDymolaOrSkip (
424424 mo = bouncingBallModel ,
425425 outdir = outdir ,
426- backend = "dymola" ,
427426 fmiType = "me" ,
428427 fmiVersion = "2" ,
429- verbose = True ,
430- force = True ,
431- dymolaConfig = _get_dymola_config (),
432428 )
433429
434430 assert result .success , f"Compilation failed: { result .error_message } "
@@ -443,15 +439,11 @@ def test_simulate_bouncing_ball_cosimulation(
443439
444440 # Compile to Co-Simulation
445441 outdir = tmp_path / "output_bb_cs"
446- result = compileFmu (
442+ result = _compileWithDymolaOrSkip (
447443 mo = bouncingBallModel ,
448444 outdir = outdir ,
449- backend = "dymola" ,
450445 fmiType = "cs" ,
451446 fmiVersion = "2" ,
452- verbose = True ,
453- force = True ,
454- dymolaConfig = _get_dymola_config (),
455447 )
456448
457449 assert result .success , f"Compilation failed: { result .error_message } "
@@ -489,15 +481,11 @@ def test_simulate_bouncing_ball_model_exchange(
489481
490482 # Compile to Model Exchange
491483 outdir = tmp_path / "output_bb_me"
492- result = compileFmu (
484+ result = _compileWithDymolaOrSkip (
493485 mo = bouncingBallModel ,
494486 outdir = outdir ,
495- backend = "dymola" ,
496487 fmiType = "me" ,
497488 fmiVersion = "2" ,
498- verbose = True ,
499- force = True ,
500- dymolaConfig = _get_dymola_config (),
501489 )
502490
503491 assert result .success , f"Compilation failed: { result .error_message } "
@@ -535,15 +523,11 @@ def test_bouncing_ball_bounce_count(self, bouncingBallModel: Path, tmp_path: Pat
535523
536524 # Compile to Co-Simulation (more reliable for event handling)
537525 outdir = tmp_path / "output_bb_count"
538- result = compileFmu (
526+ result = _compileWithDymolaOrSkip (
539527 mo = bouncingBallModel ,
540528 outdir = outdir ,
541- backend = "dymola" ,
542529 fmiType = "cs" ,
543530 fmiVersion = "2" ,
544- verbose = True ,
545- force = True ,
546- dymolaConfig = _get_dymola_config (),
547531 )
548532
549533 assert result .success
@@ -570,15 +554,11 @@ def test_bouncing_ball_fmi3_model_exchange(
570554
571555 # Compile to FMI 3.0 Model Exchange
572556 outdir = tmp_path / "output_bb_fmi3_me"
573- result = compileFmu (
557+ result = _compileWithDymolaOrSkip (
574558 mo = bouncingBallModel ,
575559 outdir = outdir ,
576- backend = "dymola" ,
577560 fmiType = "me" ,
578561 fmiVersion = "3" ,
579- verbose = True ,
580- force = True ,
581- dymolaConfig = _get_dymola_config (),
582562 )
583563
584564 # FMI 3.0 may not be supported
@@ -616,15 +596,11 @@ def test_bouncing_ball_fmi3_cosimulation(self, bouncingBallModel: Path, tmp_path
616596
617597 # Compile to FMI 3.0 Co-Simulation
618598 outdir = tmp_path / "output_bb_fmi3_cs"
619- result = compileFmu (
599+ result = _compileWithDymolaOrSkip (
620600 mo = bouncingBallModel ,
621601 outdir = outdir ,
622- backend = "dymola" ,
623602 fmiType = "cs" ,
624603 fmiVersion = "3" ,
625- verbose = True ,
626- force = True ,
627- dymolaConfig = _get_dymola_config (),
628604 )
629605
630606 # FMI 3.0 may not be supported
@@ -653,15 +629,11 @@ def test_bouncing_ball_with_wind_input(self, bouncingBallModel: Path, tmp_path:
653629
654630 # Compile to Model Exchange
655631 outdir = tmp_path / "output_bb_wind"
656- result = compileFmu (
632+ result = _compileWithDymolaOrSkip (
657633 mo = bouncingBallModel ,
658634 outdir = outdir ,
659- backend = "dymola" ,
660635 fmiType = "me" ,
661636 fmiVersion = "2" ,
662- verbose = True ,
663- force = True ,
664- dymolaConfig = _get_dymola_config (),
665637 )
666638
667639 assert result .success
0 commit comments