Skip to content

Commit

Permalink
Improve pytest infra
Browse files Browse the repository at this point in the history
  • Loading branch information
dgobalak committed Oct 10, 2024
1 parent 3180517 commit 9b339fe
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 37 deletions.
11 changes: 11 additions & 0 deletions examples/test/conftest.py
Original file line number Diff line number Diff line change
@@ -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
}
18 changes: 5 additions & 13 deletions examples/test/test_temperature_sensor.py
Original file line number Diff line number Diff line change
@@ -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()

9 changes: 9 additions & 0 deletions libs/sutsim/c/sutsim.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
5 changes: 4 additions & 1 deletion libs/sutsim/c/sutsim.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

// Supported data types for tags
typedef enum {
SUTSIM_INT32,
SUTSIM_INT32 = 0,
SUTSIM_UINT32,
SUTSIM_FLOAT,
SUTSIM_BOOL
Expand Down Expand Up @@ -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);
10 changes: 10 additions & 0 deletions sim/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
37 changes: 36 additions & 1 deletion sim/core/Simulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
#include <stdexcept>
#include <vector>

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;
Expand Down Expand Up @@ -33,8 +40,9 @@ void Simulator::init_sut(const std::string& sut_name, const std::string& lib_pat
context.sutsim_read = reinterpret_cast<bool (*)(const char*, void*, uint32_t)>(dlsym(context.lib_handle, "sutsim_read"));
context.sutsim_write = reinterpret_cast<bool (*)(const char*, const void*, uint32_t)>(dlsym(context.lib_handle, "sutsim_write"));
context.sutsim_subscribe_to_tag = reinterpret_cast<bool (*)(const char*, void*)>(dlsym(context.lib_handle, "sutsim_subscribe_to_tag"));
context.sutsim_get_tag_type = reinterpret_cast<uint8_t (*)(const char*)>(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.");
}

Expand Down Expand Up @@ -115,3 +123,30 @@ std::pair<std::string, std::string> 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<uint8_t>(sutsim_dataType_E::SUTSIM_INT32):
return "int32";
case static_cast<uint8_t>(sutsim_dataType_E::SUTSIM_UINT32):
return "uint32";
case static_cast<uint8_t>(sutsim_dataType_E::SUTSIM_FLOAT):
return "float";
case static_cast<uint8_t>(sutsim_dataType_E::SUTSIM_BOOL):
return "bool";
default:
throw std::runtime_error("Unknown data type");
}
}
2 changes: 2 additions & 0 deletions sim/core/Simulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string, SutContext> sut_contexts;

Expand Down
3 changes: 2 additions & 1 deletion sim/core/SutContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
};
34 changes: 34 additions & 0 deletions sim/py/py_binding.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "py_binding.h"
#include "Simulator.hpp"

#include <Python.h>
#include <cstring> // For strcmp
#include <stdexcept>

Expand Down Expand Up @@ -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;
}
}
12 changes: 3 additions & 9 deletions sim/py/py_binding.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@ extern "C" {

#include <stdint.h>
#include <stdbool.h>
#include <Python.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);

#ifdef __cplusplus
}
Expand Down
15 changes: 3 additions & 12 deletions sim/py/py_binding.i
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

%{
#include "py_binding.h"
#include <Python.h>
%}

// Float handling
Expand Down Expand Up @@ -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);
30 changes: 30 additions & 0 deletions sim/py/pysutsim/sim_wrapper.py
Original file line number Diff line number Diff line change
@@ -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)
10 changes: 10 additions & 0 deletions sim/py/pysutsim/sutsim_fixtures.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 9b339fe

Please sign in to comment.