From 1ae3b6185662f0788c58d16e58e43f685e79cef4 Mon Sep 17 00:00:00 2001 From: "Lars T. Kyllingstad" Date: Thu, 30 May 2024 09:53:09 +0200 Subject: [PATCH] Add some basic tests Here, I've added some basic tests for the FMI2 co-simulation interface. They don't provide full coverage, but I think they're better than nothing. More can be added as needed later. When I bumped the version number for this, I discovered a small bug in `conanfile.py` (misplaced `.strip()`) which I've also fixed. --- CMakeLists.txt | 12 ++++++ conanfile.py | 6 ++- tests/cs_slave.cpp | 64 ++++++++++++++++++++++++++++++ tests/cs_test.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++++ version.txt | 2 +- 5 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 tests/cs_slave.cpp create mode 100644 tests/cs_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f3f4fe..ac0b3fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.15) +enable_testing() file(STRINGS "${CMAKE_SOURCE_DIR}/version.txt" projectVersion) @@ -28,3 +29,14 @@ endif() install(TARGETS cppfmu ARCHIVE DESTINATION lib RUNTIME DESTINATION bin LIBRARY DESTINATION lib) install(FILES ${CMAKE_SOURCE_DIR}/cppfmu_common.hpp ${CMAKE_SOURCE_DIR}/cppfmu_cs.hpp DESTINATION ${CMAKE_INSTALL_PREFIX}/include) install(FILES ${CMAKE_SOURCE_DIR}/fmi_functions.cpp DESTINATION ${CMAKE_INSTALL_PREFIX}/src) + +if(NOT CPPFMU_FMI_1) + add_executable(cs_test + "tests/cs_test.cpp" + "tests/cs_slave.cpp" + "fmi_functions.cpp" + ) + target_compile_features(cs_test PRIVATE cxx_std_11) + target_link_libraries(cs_test PRIVATE cppfmu) + add_test(NAME "cs_test" COMMAND cs_test) +endif() diff --git a/conanfile.py b/conanfile.py index 6e5bf0d..558e877 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,10 +3,11 @@ from conan.errors import ConanInvalidConfiguration from conan.tools.build import check_min_cppstd from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout +from conan.tools.env import VirtualRunEnv from conan.tools.scm import Git, Version from conan.tools.files import copy, load, update_conandata -required_conan_version = ">=2.0" +required_conan_version = ">=2.0.15" class CppFmuConan(ConanFile): @@ -48,7 +49,7 @@ def export(self): def set_version(self): self.version = \ - load(self, path.join(self.recipe_folder, "version.txt").strip()) + load(self, path.join(self.recipe_folder, "version.txt")).strip() def config_options(self): if self.settings.os == "Windows": @@ -92,6 +93,7 @@ def build(self): cmake = CMake(self) cmake.configure() cmake.build() + cmake.ctest("--output-on-failure") def package(self): copy(self, "LICENCE.txt", self.source_folder, diff --git a/tests/cs_slave.cpp b/tests/cs_slave.cpp new file mode 100644 index 0000000..2c037d7 --- /dev/null +++ b/tests/cs_slave.cpp @@ -0,0 +1,64 @@ +#include + +#include + + +class TestSlave : public cppfmu::SlaveInstance +{ +public: + void SetReal( + const cppfmu::FMIValueReference vr[], + std::size_t nvr, + const cppfmu::FMIReal value[]) override + { + for (std::size_t i = 0; i < nvr; ++i) { + if (vr[i] == 0) { + value_ = value[i]; + } else { + throw std::logic_error("Invalid value reference"); + } + } + } + + void GetReal( + const cppfmu::FMIValueReference vr[], + std::size_t nvr, + cppfmu::FMIReal value[]) const override + { + for (std::size_t i = 0; i < nvr; ++i) { + if (vr[i] == 0) { + value[i] = value_; + } else { + throw std::logic_error("Invalid value reference"); + } + } + } + + bool DoStep( + cppfmu::FMIReal currentCommunicationPoint, + cppfmu::FMIReal communicationStepSize, + cppfmu::FMIBoolean newStep, + cppfmu::FMIReal& endOfStep) override + { + return true; + } + +private: + cppfmu::FMIReal value_ = 0.0; +}; + + +cppfmu::UniquePtr CppfmuInstantiateSlave( + cppfmu::FMIString instanceName, + cppfmu::FMIString fmuGUID, + cppfmu::FMIString fmuResourceLocation, + cppfmu::FMIString mimeType, + cppfmu::FMIReal timeout, + cppfmu::FMIBoolean visible, + cppfmu::FMIBoolean interactive, + cppfmu::Memory memory, + cppfmu::Logger logger) +{ + return cppfmu::AllocateUnique(memory); +} + diff --git a/tests/cs_test.cpp b/tests/cs_test.cpp new file mode 100644 index 0000000..05299da --- /dev/null +++ b/tests/cs_test.cpp @@ -0,0 +1,97 @@ +#include + +#include +#include +#include +#include +#include +#include + + +const double TEST_VALUE = 2.0; +const char* const TEST_INSTANCE_NAME = "MyInstance"; + + +extern "C" void logger( + fmi2ComponentEnvironment, + fmi2String instanceName, + fmi2Status status, + fmi2String category, + fmi2String message, + ...) noexcept +{ + assert(std::string(instanceName) == std::string(TEST_INSTANCE_NAME)); + va_list args; + va_start(args, message); + std::vfprintf(stderr, message, args); + va_end(args); +} + +extern "C" void* alloc(std::size_t nobj, std::size_t size) noexcept +{ + return std::malloc(nobj*size); +} + + +int main() +{ + // Instantiation and setup + const auto callbacks = fmi2CallbackFunctions{ + &logger, + &alloc, + &std::free, + nullptr, + nullptr, + }; + const auto instance = fmi2Instantiate( + TEST_INSTANCE_NAME, + fmi2CoSimulation, + "04b947f3-c057-4860-b59b-eb0bd6fa52be", + nullptr, + &callbacks, + fmi2False, + fmi2True); + assert(instance); + + const auto setupResult = fmi2SetupExperiment( + instance, + fmi2False, + 0.0, + 0.0, + fmi2False, + 0.0); + assert(setupResult == fmi2OK); + + // Initialization + const auto initResult = fmi2EnterInitializationMode(instance); + assert(initResult == fmi2OK); + + const fmi2ValueReference validVr = 0; + const auto setResult = fmi2SetReal(instance, &validVr, 1, &TEST_VALUE); + assert(setResult == fmi2OK); + + fmi2Real value = 0.0; + const auto validGetResult = fmi2GetReal(instance, &validVr, 1, &value); + assert(validGetResult == fmi2OK); + assert(value == TEST_VALUE); + + const auto endInitResult = fmi2ExitInitializationMode(instance); + assert(endInitResult == fmi2OK); + + // Simulation + const auto stepResult = fmi2DoStep(instance, 0.0,0.1, fmi2False); + assert(stepResult == fmi2OK); + + const fmi2ValueReference invalidVr = 1; + value = -1.0; + const auto invalidGetResult = fmi2GetReal(instance, &invalidVr, 1, &value); + assert(invalidGetResult == fmi2Error); + assert(value == -1.0); + + // Termination + const auto terminateResult = fmi2Terminate(instance); + assert(terminateResult == fmi2OK); + + fmi2FreeInstance(instance); + return 0; +} diff --git a/version.txt b/version.txt index afaf360..7dea76e 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.0.1