From 2072cacae5c4692a64e8f23e4ee41696954cea96 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 20 Jun 2026 07:53:01 -0400 Subject: [PATCH] Fix CI: gate MATLAB-runtime tests on engine availability; raise MATLAB floor to 0.8.1 The Core and Downgrade jobs have been red because the MATLAB runtime is not available on CI runners: - Core (julia 1 / lts): matlab_runtime_tests.jl calls solve(prob, ode45()), which opens a MATLAB MSession. On any runner without a usable MATLAB engine (all GitHub-hosted runners, and self-hosted runners without a licensed engine) MSession throws UndefRefError, erroring the test. The interface and JET tests, which exercise this package's own logic without an engine, pass. Gate the engine-dependent solve calls on an actual capability probe (MATLAB.libmx_size > 0 plus a real MSession(0) attempt): run them where a working MATLAB engine exists (and assert they succeed), skip with an @info notice where it does not. The package logic tests still run unconditionally. - Downgrade (lts): the MATLAB compat floor "0.8" resolves MATLAB v0.8.0, whose __init__ unconditionally runs `which matlab` and errors when MATLAB is absent, so MATLABDiffEq cannot even precompile. v0.8.1 introduced the deps.jl / check_deps build mechanism with a CI fallback (libmx_size = 0) that loads gracefully without MATLAB. Raise the floor to "0.8.1" (drops only the broken v0.8.0 patch) so Downgrade resolves to a loadable MATLAB. Verified locally on Julia 1.12 (the "julia 1" channel) with CI=true and no MATLAB present: Core tests pass (interface 40/40, jet 34/34, runtime tests skipped with the @info notice). MATLAB v0.8.1 confirmed to load gracefully (libmx_size == 0) on Julia 1.10 with CI=true and no MATLAB installed. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- Project.toml | 4 +- test/matlab_runtime_tests.jl | 91 +++++++++++++++++++++++------------- 2 files changed, 61 insertions(+), 34 deletions(-) diff --git a/Project.toml b/Project.toml index 540b5f0..38a8c74 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "MATLABDiffEq" uuid = "e2752cbe-bcf4-5895-8727-84ebc14a76bd" -version = "1.5.0" +version = "1.5.1" [deps] DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" @@ -11,7 +11,7 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] DiffEqBase = "6.122, 7" -MATLAB = "0.8, 0.9, 0.10" +MATLAB = "0.8.1, 0.9, 0.10" ModelingToolkit = "8, 9, 10, 11" ParameterizedFunctions = "5" PrecompileTools = "1.1" diff --git a/test/matlab_runtime_tests.jl b/test/matlab_runtime_tests.jl index 682cf16..1609092 100644 --- a/test/matlab_runtime_tests.jl +++ b/test/matlab_runtime_tests.jl @@ -1,37 +1,64 @@ -# These tests require MATLAB runtime to be available. -# They test the actual ODE solving functionality. -using DiffEqBase, MATLABDiffEq, ParameterizedFunctions +# These tests exercise the actual ODE solving functionality, which requires a +# usable MATLAB engine. They are gated on the engine being startable: on CI +# runners (and any machine without a licensed/working MATLAB) starting an +# MSession is impossible, so the engine-dependent smoke tests are skipped with a +# notice rather than failing. Where a working MATLAB engine is present the full +# solve path runs and must succeed. +using DiffEqBase, MATLABDiffEq, ParameterizedFunctions, MATLAB, Test -f = @ode_def_bare LotkaVolterra begin - dx = a * x - b * x * y - dy = -c * y + d * x * y -end a b c d -p = [1.5, 1, 3, 1] -tspan = (0.0, 10.0) -u0 = [1.0, 1.0] -prob = ODEProblem(f, u0, tspan, p) -sol = solve(prob, MATLABDiffEq.ode45()) - -function lorenz(du, u, p, t) - du[1] = 10.0(u[2] - u[1]) - du[2] = u[1] * (28.0 - u[3]) - u[2] - return du[3] = u[1] * u[2] - (8 / 3) * u[3] +# `libmx_size == 0` means MATLAB.jl was built without a MATLAB installation +# (the build-time CI fallback), so the engine libraries were never loaded. +# Even when libraries are present, starting an MSession can still fail (no +# license, no display), so probe an actual session before running the solves. +function matlab_engine_available() + MATLAB.libmx_size > 0 || return false + return try + MATLAB.MSession(0) + true + catch + false + end end -u0 = [1.0; 0.0; 0.0] -tspan = (0.0, 100.0) -prob = ODEProblem(lorenz, u0, tspan) -sol = solve(prob, MATLABDiffEq.ode45()) -algs = [ - MATLABDiffEq.ode23 - MATLABDiffEq.ode45 - MATLABDiffEq.ode113 - MATLABDiffEq.ode23s - MATLABDiffEq.ode23t - MATLABDiffEq.ode23tb - MATLABDiffEq.ode15s -] +@testset "MATLAB runtime solve" begin + if !matlab_engine_available() + @info "MATLAB engine not available on this machine; skipping engine-dependent solve tests." + else + f = @ode_def_bare LotkaVolterra begin + dx = a * x - b * x * y + dy = -c * y + d * x * y + end a b c d + p = [1.5, 1, 3, 1] + tspan = (0.0, 10.0) + u0 = [1.0, 1.0] + prob = ODEProblem(f, u0, tspan, p) + sol = solve(prob, MATLABDiffEq.ode45()) + @test length(sol.t) > 0 + + function lorenz(du, u, p, t) + du[1] = 10.0(u[2] - u[1]) + du[2] = u[1] * (28.0 - u[3]) - u[2] + return du[3] = u[1] * u[2] - (8 / 3) * u[3] + end + u0 = [1.0; 0.0; 0.0] + tspan = (0.0, 100.0) + prob = ODEProblem(lorenz, u0, tspan) + sol = solve(prob, MATLABDiffEq.ode45()) + @test length(sol.t) > 0 + + algs = [ + MATLABDiffEq.ode23 + MATLABDiffEq.ode45 + MATLABDiffEq.ode113 + MATLABDiffEq.ode23s + MATLABDiffEq.ode23t + MATLABDiffEq.ode23tb + MATLABDiffEq.ode15s + ] -for alg in algs - sol = solve(prob, alg()) + for alg in algs + sol = solve(prob, alg()) + @test length(sol.t) > 0 + end + end end