Skip to content

Commit

Permalink
Tests (ThummeTo#66)
Browse files Browse the repository at this point in the history
* Add devcontainer and Dependencies

* template for FMI2/3 tests

* s

* Unzip and Load

* switch container to 22.04

* add os and architecture distinction and check

* add passing test to os change

* Working Tests for Instantiate, Free, GetVersion and GetTypesPlatform

* create utils.jl

* os check in utils

* Get/SetStatus Tests, type checking

* typo

* SetDebugLogging

* Reset + Terminate

* FreeState

* serializedsize

* typo

* Getter and Setter

* get/set and directionalderivative

* ME-specific Functions

* move type check to utils

* Testsets

* some CS Tests

* logging?

* ähh

* working generic and ME-specific functions

* remove component typecast

* try internal callback

* fix terminate test with logger

* add os distinction to cblibpath

* add binaries for linux and mac

* add function to create callbacks

* stop logging

* CS Get/Set Tests

* Get and Set Real Input and Output Derivatives CS

* split files

* remove todo

* remove devcontainer

* add download for callbacks

* fix download dll on windows

* replace OS-Check Test with warning and skip

* add begin and end to testsets for 1.6 compatibility

* remove Type Checks and "fix" 32 Bit Tests

* Add Testing with IO FMU and Tests for fmi2String and fmi2Boolean

* move CS Get/Set OutputDerivatives to IO-FMU

* Supress expected Warnings and CVODE Stats

* point to right 32 bit binaries

* remove unnecessary DoStep that Errors on 32 Bit for some reason

* decrease dostep size

* change stepsize back

* only Test DoStep on x64

---------

Co-authored-by: TT <[email protected]>
Co-authored-by: ThummeTo <[email protected]>
  • Loading branch information
3 people authored Aug 27, 2024
1 parent 8ab6407 commit f07407f
Show file tree
Hide file tree
Showing 9 changed files with 474 additions and 19 deletions.
23 changes: 6 additions & 17 deletions src/FMI2/cfunc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,23 +91,12 @@ Source: FMISpec2.0.2[p.22]: 2.1.5 Creation, Destruction and Logging of FMU Insta
The function controls debug logging that is output via the logger function callback. If loggingOn = fmi2True, debug logging is enabled, otherwise it is switched off.
"""
function fmi2SetDebugLogging(
cfunc::Ptr{Cvoid},
c::fmi2Component,
logginOn::fmi2Boolean,
nCategories::Csize_t,
categories::Union{Ptr{fmi2String},AbstractArray{fmi2String}},
)::fmi2Status
status = ccall(
cfunc,
fmi2Status,
(fmi2Component, fmi2Boolean, Csize_t, Ptr{fmi2String}),
c,
logginOn,
nCategories,
categories,
)
@debug "fmi2SetDebugLogging(c: $(c), logginOn: $(loggingOn), nCategories: $(nCategories), categories: $(categories)) → $(status)"
function fmi2SetDebugLogging(cfunc::Ptr{Cvoid}, c::fmi2Component, loggingOn::fmi2Boolean, nCategories::Csize_t, categories::Union{Ptr{fmi2String}, AbstractArray{fmi2String}})
status = ccall(cfunc,
fmi2Status,
(fmi2Component, fmi2Boolean, Csize_t, Ptr{fmi2String}),
c, loggingOn, nCategories, categories)
@debug "fmi2SetDebugLogging(c: $(c), loggingOn: $(loggingOn), nCategories: $(nCategories), categories: $(categories)) → $(status)"
return status
end
export fmi2SetDebugLogging
Expand Down
72 changes: 72 additions & 0 deletions test/FMI2/CS.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

using Libdl, Suppressor

function test_CS(lib, cblibpath)
component = fmi2Instantiate(dlsym(lib, :fmi2Instantiate), pointer("test_cs"), fmi2TypeCoSimulation, pointer("{3c564ab6-a92a-48ca-ae7d-591f819b1d93}"), pointer("file:///"), Ptr{fmi2CallbackFunctions}(pointer_from_objref(get_callbacks(cblibpath))), fmi2Boolean(false), fmi2Boolean(false))

startpoint = fmi2Real(0.0)
@test fmi2StatusOK == fmi2SetupExperiment(dlsym(lib, :fmi2SetupExperiment),component, fmi2Boolean(true), fmi2Real(0.01), startpoint, fmi2Boolean(false), fmi2Real(1.0))


fmi2EnterInitializationMode(dlsym(lib, :fmi2EnterInitializationMode), component)
fmi2ExitInitializationMode(dlsym(lib, :fmi2ExitInitializationMode), component)
if Sys.WORD_SIZE == 64
@test fmi2StatusOK == fmi2DoStep(dlsym(lib, :fmi2DoStep), component, fmi2Real(0.0), fmi2Real(0.01), fmi2False)
end


status = fmi2Real(0.0)
statusptr = pointer([status])
@test fmi2StatusOK == fmi2GetRealStatus!(dlsym(lib, :fmi2GetRealStatus), component, fmi2StatusKindLastSuccessfulTime, Ptr{fmi2Real}(statusptr))

@suppress begin
# Suppressing the IllegalFunctionCall Warnings, as they are expected here
# Async is not supported in this FMU, so the status should be fmi2StatusDiscard
status = fmi2Status(fmi2StatusOK)
statusptr = pointer([status])
@test fmi2StatusDiscard == fmi2GetStatus!(dlsym(lib, :fmi2GetStatus), component, fmi2StatusKindDoStepStatus, Ptr{fmi2Status}(statusptr))


status = fmi2Integer(0)
statusptr = pointer([status])
# Async is not supported in this FMU, so the status should be fmi2StatusDiscard
@test fmi2StatusDiscard == fmi2GetIntegerStatus!(dlsym(lib, :fmi2GetIntegerStatus), component, Cuint(2), Ptr{fmi2Integer}(statusptr))

status = fmi2Boolean(false)
statusptr = pointer([status])
# Async is not supported in this FMU, so the status should be fmi2StatusDiscard
@test fmi2StatusDiscard == fmi2GetBooleanStatus!(dlsym(lib, :fmi2GetBooleanStatus), component, Cuint(2), Ptr{fmi2Boolean}(statusptr))

status = "test"
statusptr = pointer(status)
# Async is not supported in this FMU, so the status should be fmi2StatusDiscard
@test fmi2StatusDiscard == fmi2GetStringStatus!(dlsym(lib, :fmi2GetStringStatus), component, Cuint(2), Ptr{fmi2String}(statusptr))
end

fmi2Terminate(dlsym(lib, :fmi2Terminate), component)

end

function test_CS_IO(lib, cblibpath)
component = fmi2Instantiate(dlsym(lib, :fmi2Instantiate), pointer("test_generic_io"), fmi2TypeCoSimulation, pointer("{95a6399d-38c5-4504-b3f3-98319bd94ce6}"), pointer("file:///"), Ptr{fmi2CallbackFunctions}(pointer_from_objref(get_callbacks(cblibpath))), fmi2Boolean(false), fmi2Boolean(false))
@test component != C_NULL
@test fmi2StatusOK == fmi2SetupExperiment(dlsym(lib, :fmi2SetupExperiment),component, fmi2Boolean(false), fmi2Real(0.0), fmi2Real(0.0), fmi2Boolean(false), fmi2Real(1.0))

@test fmi2StatusOK == fmi2EnterInitializationMode(dlsym(lib, :fmi2EnterInitializationMode), component)
@test fmi2StatusOK == fmi2ExitInitializationMode(dlsym(lib, :fmi2ExitInitializationMode), component)


fmireference = [fmi2ValueReference(352321536)]
@test fmi2StatusOK == fmi2SetRealInputDerivatives(dlsym(lib, :fmi2SetRealInputDerivatives), component, fmireference, Csize_t(1), [fmi2Integer(1)], fmi2Real.([1.0]))

fmireference = [fmi2ValueReference(335544320)]
values = zeros(fmi2Real, 1)
@test fmi2StatusOK == fmi2GetRealOutputDerivatives!(dlsym(lib, :fmi2GetRealOutputDerivatives), component, fmireference, Csize_t(1), [fmi2Integer(1)], values)




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

using Libdl

function test_ME(lib, cblibpath)
component = fmi2Instantiate(dlsym(lib, :fmi2Instantiate), pointer("test_me"), fmi2TypeModelExchange, pointer("{3c564ab6-a92a-48ca-ae7d-591f819b1d93}"), pointer("file:///"), Ptr{fmi2CallbackFunctions}(pointer_from_objref(get_callbacks(cblibpath))), fmi2Boolean(false), fmi2Boolean(false))
@test component != C_NULL

fmi2EnterInitializationMode(dlsym(lib, :fmi2EnterInitializationMode), component)
fmi2ExitInitializationMode(dlsym(lib, :fmi2ExitInitializationMode), component)

@test fmi2StatusOK == fmi2EnterEventMode(dlsym(lib, :fmi2Instantiate), component)

eventInfo = fmi2EventInfo()
ptr = Ptr{fmi2EventInfo}(pointer_from_objref(eventInfo))

@test fmi2StatusOK == fmi2NewDiscreteStates!(dlsym(lib, :fmi2NewDiscreteStates), component, ptr)

@test fmi2StatusOK == fmi2EnterContinuousTimeMode(dlsym(lib, :fmi2EnterContinuousTimeMode), component)

enterEventMode = fmi2Boolean(false)
terminateSimulation = fmi2Boolean(false)
@test fmi2StatusOK == fmi2CompletedIntegratorStep!(dlsym(lib, :fmi2CompletedIntegratorStep), component, fmi2Boolean(false), pointer([enterEventMode]), pointer([terminateSimulation]))

@test fmi2StatusOK == fmi2SetTime(dlsym(lib, :fmi2SetTime), component, fmi2Real(0.0))

n_states = Csize_t(2)
state_arr = zeros(fmi2Real, 2)
@test fmi2StatusOK == fmi2GetContinuousStates!(dlsym(lib, :fmi2GetContinuousStates), component,state_arr, n_states)

state_arr[2] = 2.0
@test fmi2StatusOK == fmi2SetContinuousStates(dlsym(lib, :fmi2SetContinuousStates), component,state_arr, n_states)

state_arr = zeros(fmi2Real, 2)
@test fmi2StatusOK == fmi2GetContinuousStates!(dlsym(lib, :fmi2GetContinuousStates), component,state_arr, n_states)
@test state_arr[2] == 2.0

n_indicators = Csize_t(2)
indicator_arr = zeros(fmi2Real, 2)
@test fmi2StatusOK == fmi2GetEventIndicators!(dlsym(lib, :fmi2GetEventIndicators), component,indicator_arr, n_indicators)

nom_state_arr = zeros(fmi2Real, 2)
@test fmi2StatusOK == fmi2GetNominalsOfContinuousStates!(dlsym(lib, :fmi2GetNominalsOfContinuousStates), component, nom_state_arr, n_states)

der_arr = zeros(fmi2Real, 2)
@test fmi2StatusOK == fmi2GetDerivatives!(dlsym(lib, :fmi2GetDerivatives), component,der_arr, n_states)
# Acceleration should be equal to Gravity in this FMU
if Sys.WORD_SIZE == 64
# on 32 Bit this returns 9.81 * 10^16 which is not equal to -9.81
@test der_arr[2] -9.81
end

@test fmi2StatusOK == fmi2Terminate(dlsym(lib, :fmi2Terminate), component)
end
22 changes: 21 additions & 1 deletion test/FMI2/cfunc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,24 @@
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

# [ToDo] tests for FMI2
using Libdl

include.(["utils.jl", "ME.jl", "CS.jl", "generic.jl"])


binarypath, fmu_path, cblibpath = get_os_binaries()
iopath, io_fmu_path, iocblibpath = get_os_binaries("IO")
if binarypath != ""
lib = dlopen(binarypath)
libio = dlopen(iopath)
# Missing Tests for fmi2<Set, Get><Boolean, String> because the FMU we are testing with doesnt variables of these types
@testset "Generic Functions in ME Mode" begin test_generic(lib,cblibpath, fmi2TypeModelExchange) end
@testset "Generic Functions in ME Mode with IO FMU" begin test_generic_io(libio,iocblibpath, fmi2TypeModelExchange) end
@testset "Generic Functions in CS Mode with IO FMU" begin test_generic_io(libio,iocblibpath, fmi2TypeCoSimulation) end
@testset "Generic Functions in CS Mode" begin test_generic(lib,cblibpath, fmi2TypeCoSimulation) end
@testset "ME-specific Functions" begin test_ME(lib, cblibpath) end
@testset "CS-specific Functions" begin test_CS(lib, cblibpath) end
@testset "CS-specific Functions with IO FMU" begin test_CS_IO(libio, iocblibpath) end
else
@warn "No valid FMU binaries found for this OS. Skipping tests."
end
148 changes: 148 additions & 0 deletions test/FMI2/generic.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

using Libdl, Suppressor

function test_generic(lib, cblibpath, type::fmi2Type)
# Tests missing for fmi2<Set, Get><Boolean, String> because the FMU we are testing with doesnt have such variables

cGetTypesPlatform = dlsym(lib, :fmi2GetTypesPlatform)
test = fmi2GetTypesPlatform(cGetTypesPlatform)
@test unsafe_string(test) == "default"

## fmi2GetVersion TODO get Version from modelDescription.xml
vers = fmi2GetVersion(dlsym(lib, :fmi2GetVersion))
@test unsafe_string(vers) == "2.0"

# fmi2Instantiate

component = fmi2Instantiate(dlsym(lib, :fmi2Instantiate), pointer("test_generic"), type, pointer("{3c564ab6-a92a-48ca-ae7d-591f819b1d93}"), pointer("file:///"), Ptr{fmi2CallbackFunctions}(pointer_from_objref(get_callbacks(cblibpath))), fmi2Boolean(false), fmi2Boolean(false))
@test component != C_NULL

@test fmi2StatusOK == fmi2SetDebugLogging(dlsym(lib, :fmi2SetDebugLogging), component, fmi2False, Unsigned(0), AbstractArray{fmi2String}([]))
@test fmi2StatusOK == fmi2SetDebugLogging(dlsym(lib, :fmi2SetDebugLogging), component, fmi2True, Unsigned(0), AbstractArray{fmi2String}([]))

# fmi2SetupExperiment

@test fmi2StatusOK == fmi2SetupExperiment(dlsym(lib, :fmi2SetupExperiment),component, fmi2Boolean(false), fmi2Real(0.0), fmi2Real(0.0), fmi2Boolean(false), fmi2Real(1.0))



# get, set and free State
state = fmi2FMUstate()
stateRef = Ref(state)

@test fmi2StatusOK == fmi2GetFMUstate!(dlsym(lib, :fmi2GetFMUstate), component, stateRef)
state = stateRef[]

@test typeof(state) == fmi2FMUstate

@test fmi2StatusOK == fmi2SetFMUstate(dlsym(lib, :fmi2SetFMUstate), component, state)
stateRef = Ref(state)

size_ptr = Ref{Csize_t}(0)
@test fmi2StatusOK == fmi2SerializedFMUstateSize!(dlsym(lib, :fmi2SerializedFMUstateSize), component, state, size_ptr)
size = size_ptr[]

serializedState = Array{fmi2Byte}(zeros(size))
@test fmi2StatusOK == fmi2SerializeFMUstate!(dlsym(lib,:fmi2SerializeFMUstate), component, state, serializedState, size)

@test fmi2StatusOK == fmi2DeSerializeFMUstate!(dlsym(lib, :fmi2DeSerializeFMUstate), component, serializedState, size, stateRef)

@test stateRef[] != C_NULL
@test fmi2StatusOK == fmi2FreeFMUstate(dlsym(lib,:fmi2FreeFMUstate), component, stateRef)
@test stateRef[] == C_NULL



# # Initialization Mode

@test fmi2StatusOK == fmi2EnterInitializationMode(dlsym(lib, :fmi2EnterInitializationMode), component)

fmireference = [fmi2ValueReference(16777220)]
@test fmi2StatusOK == fmi2SetReal(dlsym(lib, :fmi2SetReal), component, fmireference, Csize_t(1), fmi2Real.([0.8]))

fmireference = [fmi2ValueReference(16777220)]
value = zeros(fmi2Real, 1)
@test fmi2StatusOK == fmi2GetReal!(dlsym(lib, :fmi2GetReal), component, fmireference, Csize_t(1), value)
@test value == fmi2Real.([0.8])

fmireference = [fmi2ValueReference(637534208)]
value = zeros(fmi2Integer, 1)
@test fmi2StatusOK == fmi2GetInteger!(dlsym(lib, :fmi2GetInteger), component, fmireference, Csize_t(1), value)

fmireference = [fmi2ValueReference(637534208)]
@test fmi2StatusOK == fmi2SetInteger(dlsym(lib, :fmi2SetInteger), component, fmireference, Csize_t(1), fmi2Integer.([typemin(fmi2Integer)]))

fmireference = [fmi2ValueReference(637534208)]
value = zeros(fmi2Integer, 1)
@test fmi2StatusOK == fmi2GetInteger!(dlsym(lib, :fmi2GetInteger), component, fmireference, Csize_t(1), value)
@test value == fmi2Integer.([typemin(fmi2Integer)])

# calculate ∂p/∂p (x)
posreference = [fmi2ValueReference(33554432)]
delta_x = fmi2Real.([randn()])
result = zeros(fmi2Real, 1)
@test fmi2StatusOK == fmi2GetDirectionalDerivative!(dlsym(lib,:fmi2GetDirectionalDerivative), component, posreference, Csize_t(1), posreference, Csize_t(1), delta_x, result)
# ∂p/∂p(x) should be just x for any x
if Sys.WORD_SIZE == 64
# GetDirectionalDerivative behaves weirdly on 32 Bit
@test result delta_x
end

@test fmi2StatusOK == fmi2ExitInitializationMode(dlsym(lib, :fmi2ExitInitializationMode), component)
@suppress begin
# Suppressing the CVODE-Stats that are printed here in CS Mode
@test fmi2StatusOK == fmi2Reset(dlsym(lib, :fmi2Reset), component)
end

# # # fmi2FreeInstance
@test isnothing(fmi2FreeInstance(dlsym(lib, :fmi2FreeInstance), component))

end

function test_generic_io(lib, cblibpath, type::fmi2Type)
component = fmi2Instantiate(dlsym(lib, :fmi2Instantiate), pointer("test_generic_io"), type, pointer("{95a6399d-38c5-4504-b3f3-98319bd94ce6}"), pointer("file:///"), Ptr{fmi2CallbackFunctions}(pointer_from_objref(get_callbacks(cblibpath))), fmi2Boolean(false), fmi2Boolean(false))
@test component != C_NULL
@test fmi2StatusOK == fmi2SetupExperiment(dlsym(lib, :fmi2SetupExperiment),component, fmi2Boolean(false), fmi2Real(0.0), fmi2Real(0.0), fmi2Boolean(false), fmi2Real(1.0))

@test fmi2StatusOK == fmi2EnterInitializationMode(dlsym(lib, :fmi2EnterInitializationMode), component)

fmireference = [fmi2ValueReference(16777216)]
@test fmi2StatusOK == fmi2SetReal(dlsym(lib, :fmi2SetReal), component, fmireference, Csize_t(1), fmi2Real.([0.8]))

value = zeros(fmi2Real, 1)
@test fmi2StatusOK == fmi2GetReal!(dlsym(lib, :fmi2GetReal), component, fmireference, Csize_t(1), value)
@test value == fmi2Real.([0.8])

fmireference = [fmi2ValueReference(16777217)]
value = zeros(fmi2Integer, 1)
@test fmi2StatusOK == fmi2GetInteger!(dlsym(lib, :fmi2GetInteger), component, fmireference, Csize_t(1), value)

@test fmi2StatusOK == fmi2SetInteger(dlsym(lib, :fmi2SetInteger), component, fmireference, Csize_t(1), fmi2Integer.([typemin(fmi2Integer)]))

value = zeros(fmi2Integer, 1)
@test fmi2StatusOK == fmi2GetInteger!(dlsym(lib, :fmi2GetInteger), component, fmireference, Csize_t(1), value)
@test value == fmi2Integer.([typemin(fmi2Integer)])

fmireference = [fmi2ValueReference(16777218)]
@test fmi2StatusOK == fmi2SetBoolean(dlsym(lib, :fmi2SetBoolean), component, fmireference, Csize_t(1), fmi2Boolean.([false]))

value = zeros(fmi2Boolean, 1)
@test fmi2StatusOK == fmi2GetBoolean!(dlsym(lib, :fmi2GetBoolean), component, fmireference, Csize_t(1), value)
@test value == fmi2Boolean.([false])

fmireference = [fmi2ValueReference(134217728)]

value = ["anything"]
valueptr = pointer.(value)
@test fmi2StatusOK == fmi2SetString(dlsym(lib, :fmi2SetString), component, fmireference, Csize_t(1), valueptr)

value = Vector{fmi2String}(undef, 1)
values = string.(zeros(1))
@test fmi2StatusOK == fmi2GetString!(dlsym(lib, :fmi2GetString), component, fmireference, Csize_t(1), value)
values[:] = unsafe_string.(value)
@test values == ["anything"]

end
Loading

0 comments on commit f07407f

Please sign in to comment.