From 9b339fee7d34c3b8552e84a4a3d24ec1ba54b113 Mon Sep 17 00:00:00 2001 From: Daniel Gobalakrishnan Date: Wed, 9 Oct 2024 22:29:01 -0400 Subject: [PATCH] Improve pytest infra --- examples/test/conftest.py | 11 +++++++ examples/test/test_temperature_sensor.py | 18 ++++-------- libs/sutsim/c/sutsim.c | 9 ++++++ libs/sutsim/c/sutsim.h | 5 +++- sim/CMakeLists.txt | 10 +++++++ sim/core/Simulator.cpp | 37 +++++++++++++++++++++++- sim/core/Simulator.hpp | 2 ++ sim/core/SutContext.hpp | 3 +- sim/py/py_binding.cpp | 34 ++++++++++++++++++++++ sim/py/py_binding.h | 12 ++------ sim/py/py_binding.i | 15 ++-------- sim/py/pysutsim/sim_wrapper.py | 30 +++++++++++++++++++ sim/py/pysutsim/sutsim_fixtures.py | 10 +++++++ 13 files changed, 159 insertions(+), 37 deletions(-) create mode 100644 examples/test/conftest.py create mode 100644 sim/py/pysutsim/sim_wrapper.py create mode 100644 sim/py/pysutsim/sutsim_fixtures.py diff --git a/examples/test/conftest.py b/examples/test/conftest.py new file mode 100644 index 0000000..ab9e8c0 --- /dev/null +++ b/examples/test/conftest.py @@ -0,0 +1,11 @@ +from sutsim_fixtures import suts +import pytest +import os + +@pytest.fixture +def device_map(): + # Dictionary to map device names to their firmware paths + return { + "device": os.path.join(os.path.dirname(__file__), 'sim_artifacts', 'libfirmware.so'), + # Add more devices and their respective firmware paths here if needed + } diff --git a/examples/test/test_temperature_sensor.py b/examples/test/test_temperature_sensor.py index 5f7acf9..689003a 100644 --- a/examples/test/test_temperature_sensor.py +++ b/examples/test/test_temperature_sensor.py @@ -1,18 +1,10 @@ -import sutsim as simulator +import sutsim import pytest -import os -def test_data_types(): - # Path to the firmware shared library - firmware_lib_path = os.path.join(os.path.dirname(__file__), 'sim_artifacts', 'libfirmware.so') - - # Initialize the simulator - simulator.initSim("device", firmware_lib_path) - - # Set the temperature sensor data and verify it was set correctly - simulator.setSutDataFloat("device.temperature_sensor.temperature", 100.0) - simulator.getSutDataFloat("device.temperature_sensor.temperature") == 100.0 +def test_temperature_sensor_reads(suts): + suts["device"]["temperature_sensor.temperature"] = 100.0 + assert suts["device"]["temperature_sensor.temperature"] == 100.0 for _ in range(25): - simulator.runTick() + sutsim.runTick() \ No newline at end of file diff --git a/libs/sutsim/c/sutsim.c b/libs/sutsim/c/sutsim.c index b0f7037..8aa8de6 100644 --- a/libs/sutsim/c/sutsim.c +++ b/libs/sutsim/c/sutsim.c @@ -118,6 +118,15 @@ void sutsim_cleanup(void) { tag_list.count = 0; } +uint8_t sutsim_get_tag_type(const char* tag) { + sutsim_tagEntry_S* tagEntry = sutsim_find_tag(tag); + if (tagEntry) { + return (uint8_t)tagEntry->type; + } + + return 0; // Tag not found +} + static sutsim_tagEntry_S* sutsim_find_tag(const char* tag) { sutsim_tagEntry_S* current = tag_list.head; while (current != NULL) { diff --git a/libs/sutsim/c/sutsim.h b/libs/sutsim/c/sutsim.h index 3f9ad9c..61cfd08 100644 --- a/libs/sutsim/c/sutsim.h +++ b/libs/sutsim/c/sutsim.h @@ -5,7 +5,7 @@ // Supported data types for tags typedef enum { - SUTSIM_INT32, + SUTSIM_INT32 = 0, SUTSIM_UINT32, SUTSIM_FLOAT, SUTSIM_BOOL @@ -52,5 +52,8 @@ bool sutsim_write(const char* tag, const void* data, uint32_t size); // Read data from the tag bool sutsim_read(const char* tag, void* buffer, uint32_t size); +// Get the data type of a tag +uint8_t sutsim_get_tag_type(const char* tag); + // Clean up the tag list void sutsim_cleanup(void); diff --git a/sim/CMakeLists.txt b/sim/CMakeLists.txt index a240d66..cec4d50 100644 --- a/sim/CMakeLists.txt +++ b/sim/CMakeLists.txt @@ -79,3 +79,13 @@ file(WRITE ${SUTSIM_TEST_DIR}/run_tests.sh file(COPY ${SUTSIM_TEST_DIR}/run_tests.sh DESTINATION ${SUTSIM_TEST_DIR} FILE_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +file(GLOB_RECURSE PY_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} py/pysutsim/*.py) + +foreach(PY_FILE ${PY_FILES}) + # Get the file name without the directory structure + get_filename_component(FILE_NAME ${PY_FILE} NAME) + + # Copy the file to the target directory without preserving the source directory structure + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${PY_FILE} ${SUTSIM_TEST_DIR}/sim_artifacts/${FILE_NAME} COPYONLY) +endforeach() \ No newline at end of file diff --git a/sim/core/Simulator.cpp b/sim/core/Simulator.cpp index 5aed0e4..ea60e25 100644 --- a/sim/core/Simulator.cpp +++ b/sim/core/Simulator.cpp @@ -4,6 +4,13 @@ #include #include +enum class sutsim_dataType_E { + SUTSIM_INT32 = 0, + SUTSIM_UINT32, + SUTSIM_FLOAT, + SUTSIM_BOOL +}; + Simulator::Simulator() { // Initialize the simulator // std::cout << "Simulator initialized" << std::endl; @@ -33,8 +40,9 @@ void Simulator::init_sut(const std::string& sut_name, const std::string& lib_pat context.sutsim_read = reinterpret_cast(dlsym(context.lib_handle, "sutsim_read")); context.sutsim_write = reinterpret_cast(dlsym(context.lib_handle, "sutsim_write")); context.sutsim_subscribe_to_tag = reinterpret_cast(dlsym(context.lib_handle, "sutsim_subscribe_to_tag")); + context.sutsim_get_tag_type = reinterpret_cast(dlsym(context.lib_handle, "sutsim_get_tag_type")); - if (!context.sutsim_init || !context.sutsim_tick || !context.sutsim_read || !context.sutsim_write || !context.sutsim_subscribe_to_tag) { + if (!context.sutsim_init || !context.sutsim_tick || !context.sutsim_read || !context.sutsim_write || !context.sutsim_subscribe_to_tag || !context.sutsim_get_tag_type) { throw std::runtime_error("Failed to load required symbols from SUT library."); } @@ -115,3 +123,30 @@ std::pair Simulator::splitTag(const std::string& tag) } return std::make_pair(tag.substr(0, pos), tag.substr(pos + 1)); } + +std::string Simulator::getSutDataType(const std::string& tag) { + std::string sut_name, sut_tag; + std::tie(sut_name, sut_tag) = splitTag(tag); + + if (sut_contexts.find(sut_name) == sut_contexts.end()) { + throw std::invalid_argument("SUT not found"); + } + + if (sut_contexts[sut_name].sutsim_get_tag_type == nullptr) { + throw std::invalid_argument("SUT does not support get data type"); + } + + uint8_t type = sut_contexts[sut_name].sutsim_get_tag_type(sut_tag.c_str()); + switch (type) { + case static_cast(sutsim_dataType_E::SUTSIM_INT32): + return "int32"; + case static_cast(sutsim_dataType_E::SUTSIM_UINT32): + return "uint32"; + case static_cast(sutsim_dataType_E::SUTSIM_FLOAT): + return "float"; + case static_cast(sutsim_dataType_E::SUTSIM_BOOL): + return "bool"; + default: + throw std::runtime_error("Unknown data type"); + } +} diff --git a/sim/core/Simulator.hpp b/sim/core/Simulator.hpp index a54d330..a5c98ae 100644 --- a/sim/core/Simulator.hpp +++ b/sim/core/Simulator.hpp @@ -16,6 +16,8 @@ class Simulator { void setSutData(const std::string& tag, const void* value, const std::string& type); void getSutData(const std::string& tag, void* value, const std::string& type); + std::string getSutDataType(const std::string& tag); + private: std::unordered_map sut_contexts; diff --git a/sim/core/SutContext.hpp b/sim/core/SutContext.hpp index 46bf49f..4b7375d 100644 --- a/sim/core/SutContext.hpp +++ b/sim/core/SutContext.hpp @@ -5,7 +5,7 @@ class SutContext { public: - SutContext() : lib_handle(nullptr), sutsim_init(nullptr), sutsim_tick(nullptr), sutsim_subscribe_to_tag(nullptr), sutsim_write(nullptr), sutsim_read(nullptr) {} + SutContext() : lib_handle(nullptr), sutsim_init(nullptr), sutsim_tick(nullptr), sutsim_subscribe_to_tag(nullptr), sutsim_write(nullptr), sutsim_read(nullptr), sutsim_get_tag_type(nullptr) {} ~SutContext() = default; std::string sut_name; @@ -17,6 +17,7 @@ class SutContext { bool (*sutsim_subscribe_to_tag)(const char*, void*); bool (*sutsim_write)(const char*, const void*, uint32_t); bool (*sutsim_read)(const char*, void*, uint32_t); + uint8_t (*sutsim_get_tag_type)(const char*); bool initialized = false; }; diff --git a/sim/py/py_binding.cpp b/sim/py/py_binding.cpp index 27401b8..6d05a6d 100644 --- a/sim/py/py_binding.cpp +++ b/sim/py/py_binding.cpp @@ -1,5 +1,7 @@ #include "py_binding.h" #include "Simulator.hpp" + +#include #include // For strcmp #include @@ -52,3 +54,35 @@ bool getSutDataBool(const char* tag) { simulator.getSutData(std::string(tag), &value, "bool"); return value; } + +void setSutData(const char* tag, PyObject* value) { + if (PyFloat_Check(value)) { + setSutDataFloat(tag, (float)PyFloat_AsDouble(value)); + } else if (PyLong_Check(value)) { + long v = PyLong_AsLong(value); + if (v >= 0) { + setSutDataUInt32(tag, (uint32_t)v); + } else { + setSutDataInt32(tag, (int32_t)v); + } + } else if (PyBool_Check(value)) { + setSutDataBool(tag, PyObject_IsTrue(value)); + } else { + PyErr_SetString(PyExc_TypeError, "Unsupported data type for setSutData"); + } +} + +PyObject* getSutData(const char* tag) { + if (simulator.getSutDataType(std::string(tag)) == "float") { + return PyFloat_FromDouble(getSutDataFloat(tag)); + } else if (simulator.getSutDataType(std::string(tag)) == "int32") { + return PyLong_FromLong(getSutDataInt32(tag)); + } else if (simulator.getSutDataType(std::string(tag)) == "uint32") { + return PyLong_FromUnsignedLong(getSutDataUInt32(tag)); + } else if (simulator.getSutDataType(std::string(tag)) == "bool") { + return PyBool_FromLong(getSutDataBool(tag)); + } else { + PyErr_SetString(PyExc_TypeError, "Unsupported data type for getSutData"); + return NULL; + } +} diff --git a/sim/py/py_binding.h b/sim/py/py_binding.h index 2f106f6..3e3b2b6 100644 --- a/sim/py/py_binding.h +++ b/sim/py/py_binding.h @@ -6,19 +6,13 @@ extern "C" { #include #include +#include void initSim(const char* sut_name, const char* lib_path); void runTick(); -void setSutDataFloat(const char* tag, float value); -void setSutDataInt32(const char* tag, int32_t value); -void setSutDataUInt32(const char* tag, uint32_t value); -void setSutDataBool(const char* tag, bool value); - -float getSutDataFloat(const char* tag); -int32_t getSutDataInt32(const char* tag); -uint32_t getSutDataUInt32(const char* tag); -bool getSutDataBool(const char* tag); +void setSutData(const char* tag, PyObject* value); +PyObject* getSutData(const char* tag); #ifdef __cplusplus } diff --git a/sim/py/py_binding.i b/sim/py/py_binding.i index d97b7df..468d262 100644 --- a/sim/py/py_binding.i +++ b/sim/py/py_binding.i @@ -2,6 +2,7 @@ %{ #include "py_binding.h" +#include %} // Float handling @@ -52,17 +53,7 @@ $result = PyBool_FromLong($1); } -// %import "py_binding.h" - void initSim(const char* sut_name, const char* lib_path); void runTick(); - -void setSutDataFloat(const char* tag, float value); -void setSutDataInt32(const char* tag, int32_t value); -void setSutDataUInt32(const char* tag, uint32_t value); -void setSutDataBool(const char* tag, bool value); - -float getSutDataFloat(const char* tag); -int32_t getSutDataInt32(const char* tag); -uint32_t getSutDataUInt32(const char* tag); -bool getSutDataBool(const char* tag); +void setSutData(const char* tag, PyObject* value); +PyObject* getSutData(const char* tag); diff --git a/sim/py/pysutsim/sim_wrapper.py b/sim/py/pysutsim/sim_wrapper.py new file mode 100644 index 0000000..02ecd4d --- /dev/null +++ b/sim/py/pysutsim/sim_wrapper.py @@ -0,0 +1,30 @@ +import sutsim + +class SimWrapper: + def __init__(self, device_map): + """ + Initialize the SimWrapper with a dictionary of devices and their corresponding firmware paths. + """ + self.devices = {} + for device_name, firmware_path in device_map.items(): + # Initialize each device with its firmware path + sutsim.initSim(device_name, firmware_path) + self.devices[device_name] = firmware_path + + def __getitem__(self, device_name): + # Check if the device exists in the initialized list + if device_name not in self.devices: + raise KeyError(f"Device '{device_name}' not found.") + return SimDevice(device_name) + +class SimDevice: + def __init__(self, device_name): + self.device_name = device_name + + def __getitem__(self, tag): + full_key = f"{self.device_name}.{tag}" + return sutsim.getSutData(full_key) + + def __setitem__(self, tag, value): + full_key = f"{self.device_name}.{tag}" + sutsim.setSutData(full_key, value) diff --git a/sim/py/pysutsim/sutsim_fixtures.py b/sim/py/pysutsim/sutsim_fixtures.py new file mode 100644 index 0000000..fb2615a --- /dev/null +++ b/sim/py/pysutsim/sutsim_fixtures.py @@ -0,0 +1,10 @@ +import sutsim +import pytest +from sim_wrapper import SimWrapper + +@pytest.fixture +def suts(device_map): + """ + Fixture to set up the simulator with the provided device map. + """ + return SimWrapper(device_map)