Skip to content

Commit

Permalink
FMPy CI - second try (#62)
Browse files Browse the repository at this point in the history
* debug version of FMPy tests

#TODO should be removed before PR

* removed debug statements

example and tests-latest actions where successfully tested on 17 Dec 2024 18:56 GMT

* added julia caching

* fix for PkgEval Xvfb issue

* version bump fix

---------

Co-authored-by: Simon Exner <0815Creeper@users.noreply.github.com>
  • Loading branch information
0815Creeper and 0815Creeper authored Dec 23, 2024
1 parent 88da6a2 commit cc55bce
Show file tree
Hide file tree
Showing 9 changed files with 310 additions and 68 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/Eval.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
julia-arch: [x64]

steps:
# Required by PkgEval.jl as xvfb runs into issues with ubuntu 24. See ci.yml workflow of PkgEval.jl repo
- name: "Allow unprivileged user namespaces"
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Check out repository
uses: actions/checkout@v4
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/Example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,17 @@ jobs:
needs: [jupyter, pluto]
if: github.event_name != 'pull_request' && github.ref_name == 'main'
runs-on: ubuntu-latest
env:
HAS_TRIGGER_TOKEN: ${{ secrets.FMI_DOC_TRIGGER_PAT != '' }}
steps:
# Trigger an repoisitory dispath event
- name: Repository Dispatch
if: ${{ env.HAS_TRIGGER_TOKEN == 'true' }}
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.FMI_DOC_TRIGGER_PAT }}
repository: 'ThummeTo/FMI.jl'
event-type: trigger-docu
- name: no-token-warning
if: ${{ env.HAS_TRIGGER_TOKEN != 'true' }}
run: echo "::warning title=no_token_for_FMI-Repo::Please trigger FMI-Docs manually!!! automatic building of documentation requires an accesstoken for FMI.jl github repository; it has to be added as secrets.FMI_DOC_TRIGGER_PAT to the FMIExport.jl repo "
4 changes: 4 additions & 0 deletions .github/workflows/TestLTS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ jobs:
version: ${{ matrix.julia-version }}
arch: ${{ matrix.julia-arch }}

# Set up julia-cache
- name: Set up julia-cache
uses: julia-actions/cache@v2

# Set up cache
- name: "Set up cache"
uses: actions/cache@v4
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/TestLatest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
fail-fast: false # keep other os running if one fails
matrix:
julia-version: ['1']
julia-arch: [x64]
os: [windows-latest] # ubuntu-latest,
os: [windows-latest, ubuntu-latest]
experimental: [false]

steps:
Expand All @@ -35,6 +35,10 @@ jobs:
version: ${{ matrix.julia-version }}
arch: ${{ matrix.julia-arch }}

# Set up julia-cache
- name: Set up julia-cache
uses: julia-actions/cache@v2

# Set up cache
- name: "Set up cache"
uses: actions/cache@v4
Expand Down Expand Up @@ -66,4 +70,4 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
file: lcov.info
files: lcov.info
4 changes: 4 additions & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
[deps]
Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
FMI = "14a09403-18e3-468f-ad8a-74f8dda2d9ac"
FMIBuild = "226f0e26-6dd6-4589-ada7-1d32f6e1d800"
FMIImport = "9fcbc62e-52a0-44e9-a616-1359a0008194"
FMIZoo = "724179cf-c260-40a9-bd27-cccc6fe2f195"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

Expand Down
55 changes: 0 additions & 55 deletions test/bouncing_ball.jl

This file was deleted.

208 changes: 208 additions & 0 deletions test/bouncing_ball/bouncing_ball.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

# export FMU script, currently only available on Windows
if Sys.iswindows()
include(
joinpath(
@__DIR__,
"..",
"..",
"examples",
"FMI2",
"BouncingBall",
"src",
"BouncingBall.jl",
),
)
# check if FMU exists now
@test isfile(fmu_save_path)
fsize = filesize(fmu_save_path) / 1024 / 1024
@test fsize > 300

# TODO: as Exported FMUs are currently not able to be simulated with FMPy, use BouncingBall from FMIZoo instead to test Pipeline
println(
"::warning title=Test-Warning::using FMIZoo BouncingBallFMU instead of exported FMU. \r\n",
)
using FMIZoo
fmu_save_path = FMIZoo.get_model_filename("BouncingBall1D", "Dymola", "2023x")
else
# if not on windows, use BouncingBall from FMIZoo
using FMIZoo
fmu_save_path = FMIZoo.get_model_filename("BouncingBall1D", "Dymola", "2023x")

# check if FMU exists
@test isfile(fmu_save_path)
fsize = filesize(fmu_save_path) / 1024 # / 1024 # check for 300KB instead of 300 MB as FMIZoo FMU is smaller
@test fsize > 300
end

# mutex implementation: indicates running state of fmpy script. File must only be created and cleared afterwards by fmpy script
lockfile = joinpath(pwd(), "bouncing_ball", "lockfile.txt")
# fmpy script puts its logs here
logfile = joinpath(pwd(), "bouncing_ball", "FMPy-log.txt")
# output for scheduled command starting the fmpy script. meight be useful for debugging if logfile does not contain any helpful information on error
outlog = joinpath(pwd(), "bouncing_ball", "outlog.txt")
# fmu-experiment setup
t_start = "0.0"
t_stop = "5.0"
# flag (in logfile), that gets replaced by "@test " by this jl script and evaluated after fmpys completion
juliatestflag = "JULIA_@test:"

# as commandline interface for task sheduling in windows does only allow 261 characters for \TR option, we need an external config file
config_file = joinpath(pwd(), "bouncing_ball", "fmpy-bouncing_ball.config")
open(config_file, "w+") do io
#line 1: lockfile
write(io, lockfile)
write(io, "\n")
#line 2: logfile
write(io, logfile)
write(io, "\n")
#line 3: fmu_save_path
write(io, fmu_save_path)
write(io, "\n")
#line 4: juliatestflag
write(io, juliatestflag)
write(io, "\n")
#line 5: t_start
write(io, t_start)
write(io, "\n")
#line 5: t_stop
write(io, t_stop)
write(io, "\n")
end
script_file = joinpath(pwd(), "bouncing_ball", "fmpy-bouncing_ball.py")

# should not exist but cleanup anyway
if isfile(lockfile)
rm(lockfile)
end
if isfile(logfile)
rm(logfile)
end

# install fmpy
println(readchomp(`python -m pip install FMPy`))

using Dates
# task can only be sheduled at full minutes, schedule with at least one full minute until start to avoid cornercases. 120s achives this optimally (seconds get truncated in minute-based-scheduling)
tasktime = now() + Second(120)
# cleanup github-actions logs
flush(stdout)
flush(stderr)

# the fmpy task that we want to schedule (its stdout and stderr get redirected for debugging, remains empty/non existent if no error occurs)
task_string = "python $script_file $config_file > $outlog 2>&1"

if Sys.iswindows()
# in windows only 261 chars are allowed as command with args
@test length(task_string) < 261
time = Dates.format(tasktime, "HH:MM")
println(
readchomp(
`SCHTASKS /CREATE /SC ONCE /TN "ExternalFMIExportTesting\\BouncingBall-FMPy" /TR "$task_string" /ST $time`,
),
)
elseif Sys.islinux()
time = Dates.format(tasktime, "M")
open("crontab_fmiexport_fmpy_bouncingball", "w+") do io
# hourly as there were issues when scheduling at fixed hour (not starting, possibly due to timzone issues or am/pm; did not investigate further)
write(io, "$time * * * * $task_string")
write(io, "\n")
end
println(readchomp(`crontab crontab_fmiexport_fmpy_bouncingball`))
end

# print schedule status for debugging
if Sys.iswindows()
println(
readchomp(
`SCHTASKS /query /tn "ExternalFMIExportTesting\\BouncingBall-FMPy" /v /fo list`,
),
)
elseif Sys.islinux()
println(readchomp(`crontab -l`))
end

# wait until task has started for shure
sleep(150)

# cleanup
rm(config_file)

# we will wait a maximum time for fmpy. usually it should be done within seconds... (keep in mind maximum runtime on github runner)
time_wait_max = datetime2unix(now()) + 900.0

# fmpy still running or generated output in its logfile
if isfile(lockfile) || isfile(logfile)
if isfile(lockfile)
println(
"FMPy-Task still running, will wait for termination or a maximum time of " *
string(round((time_wait_max - datetime2unix(now())) / 60.0, digits = 2)) *
" minutes from now.",
)
end
while isfile(lockfile) && datetime2unix(now()) < time_wait_max
sleep(10)
end

# print schedule status for debugging
if Sys.iswindows()
println(
readchomp(
`SCHTASKS /query /tn "ExternalFMIExportTesting\\BouncingBall-FMPy" /v /fo list`,
),
)
elseif Sys.islinux()
println(readchomp(`crontab -l`))
end

println("wating for FMPy-Task ended; FMPy-Task done: " * string(!isfile(lockfile)))

# sould not be existing/be empty; if there was no error, fmpy script redirected all its output to its own logfile (see FMPy_log below)
if isfile(outlog)
println("CMD output of FMPy-Task: ")
for line in readlines(outlog)
println(line)
end
println("------------------END_of_CMD_output--------------------")
end

# FMPy_log
if !isfile(logfile)
println("No log of FMPy-Task found")
@test false # error: no log by fmpy created
else
println("Log of FMPy-Task: ")
for line in readlines(logfile)
println(line)
# if there is a testflag, evaluate the line
if contains(line, juliatestflag)
eval(Meta.parse("@test " * split(line, juliatestflag)[2]))
end
end
println("------------------END_of_FMPy_log--------------------")

fmpy_log = String(read(logfile))
# if no testflags occur in log, why are we running the script?! we need testflags in the log to evaluate the result...
@test occursin(juliatestflag, fmpy_log)
end
else
println(
"Error in FMPy-testsetup: Windows task scheduler or cron did not start FMPy successfully or FMPy terminated prematurely before generating lockfile or logfile",
)
@test false
end

# cleanup scheduling
if Sys.iswindows()
println(
readchomp(`SCHTASKS /DELETE /TN ExternalFMIExportTesting\\BouncingBall-FMPy /f`),
)
elseif Sys.islinux()
println(readchomp(`crontab -r`))
end

rm(fmu_save_path)
Loading

0 comments on commit cc55bce

Please sign in to comment.