From a8ab6e23543c2aa280c9e03f969bb7cc2ca874cf Mon Sep 17 00:00:00 2001 From: "Lucas, John P." Date: Wed, 17 Sep 2025 15:15:54 -0400 Subject: [PATCH 1/6] [tryspaceorg/tryspace-lab#53] Updated makefile creating copy-sims; --- Makefile | 40 +++++++++++++++++----------- {test => docker}/Dockerfile.director | 0 {test => docker}/Dockerfile.server | 0 3 files changed, 25 insertions(+), 15 deletions(-) rename {test => docker}/Dockerfile.director (100%) rename {test => docker}/Dockerfile.server (100%) diff --git a/Makefile b/Makefile index 1457f60..580fdcf 100644 --- a/Makefile +++ b/Makefile @@ -27,16 +27,6 @@ build-server: mkdir -p $(BUILDDIR) cd $(BUILDDIR) && cmake .. $(MAKE) --no-print-directory -C $(BUILDDIR) simulith_server_standalone - # Create components directory and copy shared libraries - mkdir -p $(BUILDDIR)/components - @for dir in $(TOPDIR)/comp/*/sim/build ; do \ - if [ -d "$$dir" ] && [ -f "$$dir/Makefile" ]; then \ - cp $$dir/*.so $(BUILDDIR)/components/ 2>/dev/null || true; \ - fi; \ - done - # Copy 42 configuration to build directory for runtime - cp -r 42/InOut $(BUILDDIR)/ 2>/dev/null || true - cp -r 42/Model $(BUILDDIR)/ 2>/dev/null || true build-sim: $(MAKE) build-director @@ -48,19 +38,39 @@ build-test: $(MAKE) --no-print-directory -C $(BUILDDIR) cd $(BUILDDIR) && ctest --output-on-failure -O ctest.log +copy-sims: + @if [ ! -d "$(BUILDDIR)" ]; then \ + echo "Error: Build directory $(BUILDDIR) does not exist. Please run 'make build' first."; \ + exit 1; \ + fi + mkdir -p $(BUILDDIR)/components + @for dir in $(TOPDIR)/comp/*/sim/build ; do \ + if [ -d "$$dir" ] && [ -f "$$dir/Makefile" ]; then \ + cp $$dir/*.so $(BUILDDIR)/components/ 2>/dev/null || true; \ + fi; \ + done + cp -r 42/InOut $(BUILDDIR)/ 2>/dev/null || true + cp -r 42/Model $(BUILDDIR)/ 2>/dev/null || true + clean: rm -rf $(BUILDDIR) debug: docker run --rm -it -v $(TOPDIR):$(TOPDIR) --user $(shell id -u):$(shell id -g) --name $(CONTAINER_NAME) -w $(CURDIR) $(BUILD_IMAGE) /bin/bash -director: - $(MAKE) clean build - docker build -t $(RUNTIME_DIRECTOR_NAME) -f test/Dockerfile.director . +director: copy-sims + @if [ ! -d "$(BUILDDIR)" ]; then \ + echo "Error: Build directory $(BUILDDIR) does not exist. Please run 'make build' first."; \ + exit 1; \ + fi + docker build -t $(RUNTIME_DIRECTOR_NAME) -f docker/Dockerfile.director . server: - $(MAKE) clean build - docker build -t $(RUNTIME_SERVER_NAME) -f test/Dockerfile.server . + @if [ ! -d "$(BUILDDIR)" ]; then \ + echo "Error: Build directory $(BUILDDIR) does not exist. Please run 'make build' first."; \ + exit 1; \ + fi + docker build -t $(RUNTIME_SERVER_NAME) -f docker/Dockerfile.server . stop: docker ps --filter name=tryspace-* | xargs docker stop diff --git a/test/Dockerfile.director b/docker/Dockerfile.director similarity index 100% rename from test/Dockerfile.director rename to docker/Dockerfile.director diff --git a/test/Dockerfile.server b/docker/Dockerfile.server similarity index 100% rename from test/Dockerfile.server rename to docker/Dockerfile.server From dc4027c31fb81cfc86642c09acd900fbd77364ef Mon Sep 17 00:00:00 2001 From: "Lucas, John P." Date: Wed, 17 Sep 2025 15:27:04 -0400 Subject: [PATCH 2/6] [tryspaceorg/tryspace-lab#55] Added coverage to `make test`; --- CMakeLists.txt | 18 ++- Makefile | 8 +- test/CMakeLists.txt | 8 +- test/test_gpio.c | 207 ------------------------ test/test_i2c.c | 78 --------- test/test_pwm.c | 145 ----------------- test/test_simulith.c | 18 +-- test/test_transport.c | 20 ++- test/test_uart.c | 151 ----------------- {unity => test/unity}/unity.c | 0 {unity => test/unity}/unity.h | 0 {unity => test/unity}/unity_internals.h | 0 12 files changed, 42 insertions(+), 611 deletions(-) delete mode 100644 test/test_gpio.c delete mode 100644 test/test_i2c.c delete mode 100644 test/test_pwm.c delete mode 100644 test/test_uart.c rename {unity => test/unity}/unity.c (100%) rename {unity => test/unity}/unity.h (100%) rename {unity => test/unity}/unity_internals.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f7a3b5..d764467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.25) project(Simulith VERSION 0.0 LANGUAGES C) +option(BUILD_SIMULITH_TESTS "Build Simulith tests" OFF) + # Enable compile_commands.json for IDEs and static analysis set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -22,12 +24,14 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") add_compile_options(-Wall -Werror) endif() -# Create Unity library with specific compiler flags and always build with -fPIC -add_library(unity STATIC unity/unity.c) -set_target_properties(unity PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(unity PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unity) -if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(unity PRIVATE -Wno-float-equal) +if(BUILD_SIMULITH_TESTS) + # Create Unity library with specific compiler flags and always build with -fPIC + add_library(unity STATIC ./test/unity/unity.c) + set_target_properties(unity PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_include_directories(unity PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unity) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(unity PRIVATE -Wno-float-equal) + endif() endif() # Simulith library sources @@ -162,9 +166,7 @@ set_target_properties(simulith_server_standalone PROPERTIES INSTALL_RPATH "$ORIGIN" ) - # Optionally add tests subdirectory -option(BUILD_SIMULITH_TESTS "Build Simulith tests" OFF) if(BUILD_SIMULITH_TESTS) enable_testing() add_subdirectory(test) diff --git a/Makefile b/Makefile index 580fdcf..fffb188 100644 --- a/Makefile +++ b/Makefile @@ -34,9 +34,15 @@ build-sim: build-test: mkdir -p $(BUILDDIR) - cd $(BUILDDIR) && cmake .. -DBUILD_SIMULITH_TESTS=ON + cd $(BUILDDIR) && cmake .. -DBUILD_SIMULITH_TESTS=ON -DENABLE_UNIT_TESTS=true $(MAKE) --no-print-directory -C $(BUILDDIR) cd $(BUILDDIR) && ctest --output-on-failure -O ctest.log + lcov -c --directory . --output-file $(BUILDDIR)/coverage.info + genhtml $(BUILDDIR)/coverage.info --output-directory $(BUILDDIR)/report + @echo "" + @echo "Review coverage report: " + @echo " firefox $(BUILDDIR)/report/index.html" + @echo "" copy-sims: @if [ ! -d "$(BUILDDIR)" ]; then \ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ff8c6b4..15cdf20 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,6 @@ # Unity source and include path -set(UNITY_SRC ../unity/unity.c) -set(UNITY_INC ../unity) +set(UNITY_SRC ./unity/unity.c) +set(UNITY_INC ./unity) # Include paths for tests include_directories( @@ -12,9 +12,9 @@ include_directories( # Main simulith tests executable add_executable(test_simulith test_simulith.c ${UNITY_SRC}) target_link_libraries(test_simulith simulith ${ZeroMQ_LIBRARIES} pthread) -add_test(NAME SimulithTest COMMAND test_simulith) +add_test(NAME SimulithTests COMMAND test_simulith) # Transport tests executable add_executable(test_transport test_transport.c ${UNITY_SRC}) target_link_libraries(test_transport simulith ${ZeroMQ_LIBRARIES}) -add_test(NAME TransportTest COMMAND test_transport) +add_test(NAME TransportTests COMMAND test_transport) diff --git a/test/test_gpio.c b/test/test_gpio.c deleted file mode 100644 index 04748b6..0000000 --- a/test/test_gpio.c +++ /dev/null @@ -1,207 +0,0 @@ -#include "simulith_gpio.h" -#include "unity.h" -#include - -static gpio_device_t gpio_a_devices[8]; // Server side devices -static gpio_device_t gpio_b_devices[8]; // Client side devices - -void setUp(void) -{ - memset(gpio_a_devices, 0, sizeof(gpio_a_devices)); - memset(gpio_b_devices, 0, sizeof(gpio_b_devices)); -} - -void tearDown(void) -{ - // Close all devices - for (int i = 0; i < 8; i++) - { - simulith_gpio_close(&gpio_a_devices[i]); - simulith_gpio_close(&gpio_b_devices[i]); - } -} - -void test_gpio_device_init(void) -{ - int result; - - // Test NULL device - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_init(NULL)); - - // Example: GPIO pin 0, A (server) and B (client) - strcpy(gpio_a_devices[0].name, "gpio0_a"); - strcpy(gpio_a_devices[0].address, "tcp://127.0.0.1:9000"); - gpio_a_devices[0].is_server = 1; - gpio_a_devices[0].pin = 0; - gpio_a_devices[0].direction = GPIO_OUTPUT; - result = simulith_gpio_init(&gpio_a_devices[0]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - - strcpy(gpio_b_devices[0].name, "gpio0_b"); - strcpy(gpio_b_devices[0].address, "tcp://127.0.0.1:9000"); - gpio_b_devices[0].is_server = 0; - gpio_b_devices[0].pin = 0; - gpio_b_devices[0].direction = GPIO_INPUT; - result = simulith_gpio_init(&gpio_b_devices[0]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - - // Test double initialization (should succeed for already initialized device) - result = simulith_gpio_init(&gpio_a_devices[0]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - - // Example: GPIO pin 1, A (server) and B (client) - strcpy(gpio_a_devices[1].name, "gpio1_a"); - strcpy(gpio_a_devices[1].address, "tcp://127.0.0.1:9001"); - gpio_a_devices[1].is_server = 1; - gpio_a_devices[1].pin = 1; - gpio_a_devices[1].direction = GPIO_OUTPUT; - result = simulith_gpio_init(&gpio_a_devices[1]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - - strcpy(gpio_b_devices[1].name, "gpio1_b"); - strcpy(gpio_b_devices[1].address, "tcp://127.0.0.1:9001"); - gpio_b_devices[1].is_server = 0; - gpio_b_devices[1].pin = 1; - gpio_b_devices[1].direction = GPIO_INPUT; - result = simulith_gpio_init(&gpio_b_devices[1]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); -} - -void test_gpio_device_write_read(void) -{ - gpio_device_t uninitialized_device = {0}; - - // Test operations on uninitialized device - uint8_t value; - - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_write(&uninitialized_device, 1)); - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_read(&uninitialized_device, &value)); - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_toggle(&uninitialized_device)); - - // Test NULL device - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_write(NULL, 1)); - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_read(NULL, &value)); - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_toggle(NULL)); - - // Test NULL value pointer - gpio_device_t device = {0}; - device.init = SIMULITH_GPIO_INITIALIZED; - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_read(&device, NULL)); -} - -void test_gpio_communication(void) -{ - int result; - uint8_t value; - - // Initialize A side (server) as output - strcpy(gpio_a_devices[0].name, "gpio0_a"); - strcpy(gpio_a_devices[0].address, "tcp://127.0.0.1:9000"); - gpio_a_devices[0].is_server = 1; - gpio_a_devices[0].pin = 0; - gpio_a_devices[0].direction = GPIO_OUTPUT; - result = simulith_gpio_init(&gpio_a_devices[0]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - - // Initialize B side (client) as input - strcpy(gpio_b_devices[0].name, "gpio0_b"); - strcpy(gpio_b_devices[0].address, "tcp://127.0.0.1:9000"); - gpio_b_devices[0].is_server = 0; - gpio_b_devices[0].pin = 0; - gpio_b_devices[0].direction = GPIO_INPUT; - result = simulith_gpio_init(&gpio_b_devices[0]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - - // Give ZMQ time to establish connection - usleep(100000); // 100ms delay - - // Test basic write operations - result = simulith_gpio_write(&gpio_a_devices[0], 1); - TEST_ASSERT_TRUE(result == SIMULITH_GPIO_SUCCESS || result == SIMULITH_GPIO_ERROR); // May fail due to peer communication - - // Test basic read operations (will return 0 if no data, negative on error) - result = simulith_gpio_read(&gpio_b_devices[0], &value); - TEST_ASSERT_TRUE(result == SIMULITH_GPIO_SUCCESS || result == SIMULITH_GPIO_ERROR); // May fail due to peer communication - - // Test toggle - result = simulith_gpio_toggle(&gpio_a_devices[0]); - TEST_ASSERT_TRUE(result == SIMULITH_GPIO_SUCCESS || result == SIMULITH_GPIO_ERROR); // May fail due to peer communication - - // Test invalid write value - result = simulith_gpio_write(&gpio_a_devices[0], 2); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_ERROR, result); - - // Close devices - result = simulith_gpio_close(&gpio_a_devices[0]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - - result = simulith_gpio_close(&gpio_b_devices[0]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); -} - -void test_gpio_device_close(void) -{ - gpio_device_t device = {0}; - - // Test close on uninitialized device - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_close(&device)); - - // Test close on NULL device - TEST_ASSERT_EQUAL_INT(SIMULITH_GPIO_ERROR, simulith_gpio_close(NULL)); -} - -void test_gpio_multiple_devices(void) -{ - int result; - - // Initialize multiple GPIO device pairs (different pins) - for (int i = 0; i < 3; i++) { - // Server side - sprintf(gpio_a_devices[i].name, "gpio%d_a", i); - sprintf(gpio_a_devices[i].address, "tcp://127.0.0.1:%d", 9010 + i); - gpio_a_devices[i].is_server = 1; - gpio_a_devices[i].pin = i; - gpio_a_devices[i].direction = GPIO_OUTPUT; - - result = simulith_gpio_init(&gpio_a_devices[i]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - - // Client side - sprintf(gpio_b_devices[i].name, "gpio%d_b", i); - sprintf(gpio_b_devices[i].address, "tcp://127.0.0.1:%d", 9010 + i); - gpio_b_devices[i].is_server = 0; - gpio_b_devices[i].pin = i; - gpio_b_devices[i].direction = GPIO_INPUT; - - result = simulith_gpio_init(&gpio_b_devices[i]); - TEST_ASSERT_EQUAL(SIMULITH_GPIO_SUCCESS, result); - } - - // Give ZMQ time to establish connections - usleep(100000); // 100ms delay - - // Test basic operation on each - for (int i = 0; i < 3; i++) { - result = simulith_gpio_write(&gpio_a_devices[i], 1); - TEST_ASSERT_TRUE(result == SIMULITH_GPIO_SUCCESS || result == SIMULITH_GPIO_ERROR); // May fail due to peer communication - } - - // Clean up - for (int i = 0; i < 3; i++) { - simulith_gpio_close(&gpio_a_devices[i]); - simulith_gpio_close(&gpio_b_devices[i]); - } -} - -int main(void) -{ - UNITY_BEGIN(); - - RUN_TEST(test_gpio_device_init); - RUN_TEST(test_gpio_device_write_read); - RUN_TEST(test_gpio_communication); - RUN_TEST(test_gpio_device_close); - RUN_TEST(test_gpio_multiple_devices); - - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_i2c.c b/test/test_i2c.c deleted file mode 100644 index 5555576..0000000 --- a/test/test_i2c.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "simulith_i2c.h" -#include "unity.h" - -void setUp(void) -{ - // Setup code if needed -} - -void tearDown(void) -{ - // Cleanup code if needed -} - -void test_i2c_device_init(void) -{ - i2c_device_t device = {0}; - - // Set up device parameters - snprintf(device.name, sizeof(device.name), "TestI2C"); - snprintf(device.address, sizeof(device.address), "tcp://127.0.0.1:7050"); - device.is_server = 0; - device.bus_id = 0; - device.device_addr = 0x50; - - // Test NULL device - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_init(NULL)); - - // Test device initialization (will fail without actual ZMQ endpoint, but tests the flow) - int result = simulith_i2c_init(&device); - // Since we don't have a peer, this will likely fail with connection error, which is expected - // The important thing is that it doesn't crash and returns proper error code - TEST_ASSERT_TRUE(result == SIMULITH_I2C_SUCCESS || result == SIMULITH_I2C_ERROR); - - // Clean up if it was successful - if (result == SIMULITH_I2C_SUCCESS) { - simulith_i2c_close(&device); - } -} - -void test_i2c_device_write_read(void) -{ - i2c_device_t device = {0}; - - // Test operations on uninitialized device - uint8_t test_data[] = {0xAA, 0xBB}; - uint8_t read_buffer[10]; - - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_write(&device, test_data, 2)); - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_read(&device, read_buffer, 2)); - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_transaction(&device, test_data, 2, read_buffer, 2)); - - // Test NULL device - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_write(NULL, test_data, 2)); - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_read(NULL, read_buffer, 2)); - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_transaction(NULL, test_data, 2, read_buffer, 2)); -} - -void test_i2c_device_close(void) -{ - i2c_device_t device = {0}; - - // Test close on uninitialized device - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_close(&device)); - - // Test close on NULL device - TEST_ASSERT_EQUAL_INT(SIMULITH_I2C_ERROR, simulith_i2c_close(NULL)); -} - -int main(void) -{ - UNITY_BEGIN(); - - RUN_TEST(test_i2c_device_init); - RUN_TEST(test_i2c_device_write_read); - RUN_TEST(test_i2c_device_close); - - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_pwm.c b/test/test_pwm.c deleted file mode 100644 index ac52494..0000000 --- a/test/test_pwm.c +++ /dev/null @@ -1,145 +0,0 @@ -#include "simulith_pwm.h" -#include "unity.h" -#include - -void setUp(void) -{ - // Nothing to do -} - -void tearDown(void) -{ - // Clean up any initialized channels - for (uint8_t channel = 0; channel < SIMULITH_PWM_MAX_CHANNELS; channel++) - { - simulith_pwm_close(channel); - } -} - -void test_pwm_init(void) -{ - simulith_pwm_config_t config = {.frequency_hz = 1000, .duty_cycle = 50}; - - // Test invalid channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_init(SIMULITH_PWM_MAX_CHANNELS, &config)); - - // Test NULL config - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_init(0, NULL)); - - // Test invalid frequency - config.frequency_hz = 0; // Below minimum - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_init(0, &config)); - config.frequency_hz = SIMULITH_PWM_MAX_FREQ_HZ + 1; // Above maximum - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_init(0, &config)); - - // Test invalid duty cycle - config.frequency_hz = 1000; - config.duty_cycle = 101; // Above 100% - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_init(0, &config)); - - // Test successful initialization - config.duty_cycle = 50; - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_init(0, &config)); - - // Test duplicate initialization - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_init(0, &config)); -} - -void test_pwm_start_stop(void) -{ - simulith_pwm_config_t config = {.frequency_hz = 1000, .duty_cycle = 50}; - - // Test start/stop on uninitialized channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_start(0)); - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_stop(0)); - - // Initialize channel - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_init(0, &config)); - - // Test start/stop sequence - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_start(0)); - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_stop(0)); - - // Test invalid channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_start(SIMULITH_PWM_MAX_CHANNELS)); - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_stop(SIMULITH_PWM_MAX_CHANNELS)); -} - -void test_pwm_duty_cycle(void) -{ - simulith_pwm_config_t config = {.frequency_hz = 1000, .duty_cycle = 50}; - - // Initialize channel - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_init(0, &config)); - - // Test valid duty cycle changes - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_set_duty(0, 0)); // 0% - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_set_duty(0, 100)); // 100% - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_set_duty(0, 25)); // 25% - - // Test invalid duty cycle - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_set_duty(0, 101)); - - // Test uninitialized channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_set_duty(1, 50)); - - // Test invalid channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_set_duty(SIMULITH_PWM_MAX_CHANNELS, 50)); -} - -void test_pwm_frequency(void) -{ - simulith_pwm_config_t config = {.frequency_hz = 1000, .duty_cycle = 50}; - - // Initialize channel - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_init(0, &config)); - - // Test valid frequency changes - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_set_frequency(0, SIMULITH_PWM_MIN_FREQ_HZ)); - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_set_frequency(0, SIMULITH_PWM_MAX_FREQ_HZ)); - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_set_frequency(0, 10000)); - - // Test invalid frequencies - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_set_frequency(0, 0)); - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_set_frequency(0, SIMULITH_PWM_MAX_FREQ_HZ + 1)); - - // Test uninitialized channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_set_frequency(1, 1000)); - - // Test invalid channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_set_frequency(SIMULITH_PWM_MAX_CHANNELS, 1000)); -} - -void test_pwm_close(void) -{ - simulith_pwm_config_t config = {.frequency_hz = 1000, .duty_cycle = 50}; - - // Initialize and start channel - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_init(0, &config)); - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_start(0)); - - // Close channel - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_close(0)); - - // Verify channel can be reinitialized - TEST_ASSERT_EQUAL_INT(0, simulith_pwm_init(0, &config)); - - // Test closing uninitialized channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_close(1)); - - // Test invalid channel - TEST_ASSERT_EQUAL_INT(-1, simulith_pwm_close(SIMULITH_PWM_MAX_CHANNELS)); -} - -int main(void) -{ - UNITY_BEGIN(); - - RUN_TEST(test_pwm_init); - RUN_TEST(test_pwm_start_stop); - RUN_TEST(test_pwm_duty_cycle); - RUN_TEST(test_pwm_frequency); - RUN_TEST(test_pwm_close); - - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_simulith.c b/test/test_simulith.c index 0e02e6b..9c2e20e 100644 --- a/test/test_simulith.c +++ b/test/test_simulith.c @@ -20,19 +20,19 @@ void tearDown(void) } -void on_tick(uint64_t time_ns) +static void on_tick(uint64_t time_ns) { ticks_received++; } -void *server_thread(void *arg) +static void *server_thread(void *arg) { simulith_server_init(LOCAL_PUB_ADDR, LOCAL_REP_ADDR, 1, INTERVAL_NS); simulith_server_run(); // runs indefinitely return NULL; } -void *client_thread(void *arg) +static void *client_thread(void *arg) { usleep(1000); // Wait for server to be ready @@ -49,7 +49,7 @@ void *client_thread(void *arg) return NULL; } -void test_synchronization_tick_exchange(void) +static void test_synchronization_tick_exchange(void) { pthread_t server, client; @@ -76,7 +76,7 @@ void test_synchronization_tick_exchange(void) } // Test invalid server initialization -void test_server_init_invalid_address(void) +static void test_server_init_invalid_address(void) { int result = simulith_server_init(INVALID_ADDR, LOCAL_REP_ADDR, 1, INTERVAL_NS); TEST_ASSERT_EQUAL_INT(-1, result); @@ -85,7 +85,7 @@ void test_server_init_invalid_address(void) TEST_ASSERT_EQUAL_INT(-1, result); } -void test_server_init_invalid_params(void) +static void test_server_init_invalid_params(void) { int result = simulith_server_init(LOCAL_PUB_ADDR, LOCAL_REP_ADDR, 0, INTERVAL_NS); TEST_ASSERT_EQUAL_INT(-1, result); @@ -98,7 +98,7 @@ void test_server_init_invalid_params(void) } // Test invalid client initialization -void test_client_init_invalid_address(void) +static void test_client_init_invalid_address(void) { int result = simulith_client_init(INVALID_ADDR, LOCAL_REP_ADDR, CLIENT_ID, INTERVAL_NS); TEST_ASSERT_EQUAL_INT(-1, result); @@ -107,7 +107,7 @@ void test_client_init_invalid_address(void) TEST_ASSERT_EQUAL_INT(-1, result); } -void test_client_init_invalid_params(void) +static void test_client_init_invalid_params(void) { int result = simulith_client_init(LOCAL_PUB_ADDR, LOCAL_REP_ADDR, NULL, INTERVAL_NS); TEST_ASSERT_EQUAL_INT(-1, result); @@ -120,7 +120,7 @@ void test_client_init_invalid_params(void) } // Test handshake without server -void test_client_handshake_no_server(void) +static void test_client_handshake_no_server(void) { simulith_client_init(LOCAL_PUB_ADDR, LOCAL_REP_ADDR, CLIENT_ID, INTERVAL_NS); int result = simulith_client_handshake(); diff --git a/test/test_transport.c b/test/test_transport.c index 05a3f9d..04501de 100644 --- a/test/test_transport.c +++ b/test/test_transport.c @@ -21,7 +21,7 @@ void tearDown(void) } } -void test_transport_init(void) +static void test_transport_init(void) { int result; @@ -66,7 +66,7 @@ void test_transport_init(void) TEST_ASSERT_EQUAL(SIMULITH_TRANSPORT_SUCCESS, result); } -void test_transport_send_receive(void) +static void test_transport_send_receive(void) { int result; uint8_t test_data[] = {0xAA, 0xBB, 0xCC}; @@ -114,7 +114,7 @@ void test_transport_send_receive(void) TEST_ASSERT_EQUAL(sizeof(test_data), result); } -void test_transport_buffer_overflow(void) +static void test_transport_buffer_overflow(void) { /* Initialize a pair */ strcpy(transport_a_ports[2].name, "tp2_a"); @@ -150,7 +150,7 @@ void test_transport_buffer_overflow(void) free(bigbuf); } -void test_transport_uninitialized_send(void) +static void test_transport_uninitialized_send(void) { transport_port_t uninit = {0}; uint8_t buf[4] = {1,2,3,4}; @@ -158,7 +158,7 @@ void test_transport_uninitialized_send(void) TEST_ASSERT_EQUAL(SIMULITH_TRANSPORT_ERROR, rc); } -void test_transport_multiple_messages(void) +static void test_transport_multiple_messages(void) { /* Initialize a pair */ strcpy(transport_a_ports[3].name, "tp3_a"); @@ -195,7 +195,7 @@ void test_transport_multiple_messages(void) } } -void test_transport_partial_receive(void) +static void test_transport_partial_receive(void) { /* Initialize a pair */ strcpy(transport_a_ports[4].name, "tp4_a"); @@ -241,7 +241,7 @@ void test_transport_partial_receive(void) free(big); } -void test_transport_flush(void) +static void test_transport_flush(void) { transport_port_t a = {0}; strcpy(a.name, "flush_a"); @@ -252,7 +252,7 @@ void test_transport_flush(void) simulith_transport_close(&a); } -void test_transport_close_uninitialized(void) +static void test_transport_close_uninitialized(void) { transport_port_t p = {0}; int rc = simulith_transport_close(&p); @@ -266,5 +266,9 @@ int main(void) RUN_TEST(test_transport_send_receive); RUN_TEST(test_transport_buffer_overflow); RUN_TEST(test_transport_uninitialized_send); + RUN_TEST(test_transport_multiple_messages); + RUN_TEST(test_transport_partial_receive); + RUN_TEST(test_transport_flush); + RUN_TEST(test_transport_close_uninitialized); return UNITY_END(); } diff --git a/test/test_uart.c b/test/test_uart.c deleted file mode 100644 index 8d5e72d..0000000 --- a/test/test_uart.c +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include "simulith_uart.h" - -static uart_port_t uart_a_ports[8]; -static uart_port_t uart_b_ports[8]; - -void setUp(void) -{ - printf("\ntest_uart.c setUp..\n"); - memset(uart_a_ports, 0, sizeof(uart_a_ports)); - memset(uart_b_ports, 0, sizeof(uart_b_ports)); -} - -void tearDown(void) -{ - printf("\ntest_uart.c tearDown..\n"); - // Close all ports - for (int i = 0; i < 8; i++) - { - simulith_uart_close(&uart_a_ports[i]); - simulith_uart_close(&uart_b_ports[i]); - } -} - -void test_uart_init(void) -{ - int result; - - // Example: port 0, A (server) and B (client) - strcpy(uart_a_ports[0].name, "uart0_a"); - strcpy(uart_a_ports[0].address, "tcp://127.0.0.1:6000"); - uart_a_ports[0].is_server = 1; - result = simulith_uart_init(&uart_a_ports[0]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); - - strcpy(uart_b_ports[0].name, "uart0_b"); - strcpy(uart_b_ports[0].address, "tcp://127.0.0.1:6000"); - uart_b_ports[0].is_server = 0; - result = simulith_uart_init(&uart_b_ports[0]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); - - // Example: port 1, A (server) and B (client) - strcpy(uart_a_ports[1].name, "uart1_a"); - strcpy(uart_a_ports[1].address, "tcp://127.0.0.1:6001"); - uart_a_ports[1].is_server = 1; - result = simulith_uart_init(&uart_a_ports[1]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); - - strcpy(uart_b_ports[1].name, "uart1_b"); - strcpy(uart_b_ports[1].address, "tcp://127.0.0.1:6001"); - uart_b_ports[1].is_server = 0; - result = simulith_uart_init(&uart_b_ports[1]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); - - // Last port - int last = 8-1; - sprintf(uart_a_ports[last].name, "uart%d_a", last); - sprintf(uart_a_ports[last].address, "tcp://127.0.0.1:%d", 6000+last); - uart_a_ports[last].is_server = 1; - result = simulith_uart_init(&uart_a_ports[last]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); - - sprintf(uart_b_ports[last].name, "uart%d_b", last); - sprintf(uart_b_ports[last].address, "tcp://127.0.0.1:%d", 6000+last); - uart_b_ports[last].is_server = 0; - result = simulith_uart_init(&uart_b_ports[last]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); -} - -void test_uart_send_receive(void) -{ - int result; - uint8_t test_data[] = {0x12, 0x34, 0x56}; - uint8_t rx_data[sizeof(test_data)]; - - // Initialize A side (server) - strcpy(uart_a_ports[0].name, "uart0_a"); - strcpy(uart_a_ports[0].address, "tcp://127.0.0.1:6000"); - uart_a_ports[0].is_server = 1; - result = simulith_uart_init(&uart_a_ports[0]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); - - // Initialize B side (client) - strcpy(uart_b_ports[0].name, "uart0_b"); - strcpy(uart_b_ports[0].address, "tcp://127.0.0.1:6000"); - uart_b_ports[0].is_server = 0; - result = simulith_uart_init(&uart_b_ports[0]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); - - // Delay after initialization to allow ZMQ connection to establish - printf("A and B initialized, waiting a second for ZMQ connection to establish...\n"); - sleep(1); - - // Send from A to B - result = simulith_uart_send(&uart_a_ports[0], test_data, sizeof(test_data)); - TEST_ASSERT_EQUAL(sizeof(test_data), result); - usleep(10000); // Allow time to receive - - // Confirm available on A (should be 0) - result = simulith_uart_available(&uart_a_ports[0]); - TEST_ASSERT_EQUAL(0, result); - - // Confirm available on B (should be 1 if data is ready) - result = simulith_uart_available(&uart_b_ports[0]); - TEST_ASSERT_TRUE(result == 1); - - // Receive data on B - result = simulith_uart_receive(&uart_b_ports[0], rx_data, sizeof(rx_data)); - TEST_ASSERT_EQUAL(sizeof(test_data), result); - - // Send from B to A - result = simulith_uart_send(&uart_b_ports[0], rx_data, sizeof(rx_data)); - TEST_ASSERT_EQUAL(sizeof(rx_data), result); - usleep(10000); // Allow time to receive - - // Confirm available on A - result = simulith_uart_available(&uart_a_ports[0]); - TEST_ASSERT_TRUE(result == 1); - - // Confirm available on B - result = simulith_uart_available(&uart_b_ports[0]); - TEST_ASSERT_EQUAL(0, result); - - // Receive data on A - result = simulith_uart_receive(&uart_a_ports[0], rx_data, sizeof(rx_data)); - TEST_ASSERT_EQUAL(sizeof(test_data), result); - - // Confirm available on A - result = simulith_uart_available(&uart_a_ports[0]); - TEST_ASSERT_EQUAL(0, result); - - // Confirm available on B - result = simulith_uart_available(&uart_b_ports[0]); - TEST_ASSERT_EQUAL(0, result); - - // Close A - result = simulith_uart_close(&uart_a_ports[0]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); - - // Close B - result = simulith_uart_close(&uart_b_ports[0]); - TEST_ASSERT_EQUAL(SIMULITH_UART_SUCCESS, result); -} - -int main(void) -{ - UNITY_BEGIN(); - RUN_TEST(test_uart_init); - RUN_TEST(test_uart_send_receive); - return UNITY_END(); -} \ No newline at end of file diff --git a/unity/unity.c b/test/unity/unity.c similarity index 100% rename from unity/unity.c rename to test/unity/unity.c diff --git a/unity/unity.h b/test/unity/unity.h similarity index 100% rename from unity/unity.h rename to test/unity/unity.h diff --git a/unity/unity_internals.h b/test/unity/unity_internals.h similarity index 100% rename from unity/unity_internals.h rename to test/unity/unity_internals.h From 872b2a92e770ba6ca2d26234491903ef172a5f12 Mon Sep 17 00:00:00 2001 From: "Lucas, John P." Date: Wed, 17 Sep 2025 19:54:45 -0400 Subject: [PATCH 3/6] [tryspaceorg/tryspace-lab#55] Added tests for 42, common, and time; --- CMakeLists.txt | 5 ++ Makefile | 3 +- include/simulith.h | 6 ++ src/simulith_common.c | 27 ++++++- src/simulith_server.c | 35 +++++++++ test/CMakeLists.txt | 19 ++++- test/test_42.c | 163 ++++++++++++++++++++++++++++++++++++++++++ test/test_common.c | 147 +++++++++++++++++++++++++++++++++++++ test/test_simulith.c | 160 +++++++++++++++++++++++++++++++++++++++++ test/test_time.c | 55 ++++++++++++++ 10 files changed, 616 insertions(+), 4 deletions(-) create mode 100644 test/test_42.c create mode 100644 test/test_common.c create mode 100644 test/test_time.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d764467..807d4a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,11 @@ target_include_directories(simulith PUBLIC ) target_link_libraries(simulith PUBLIC ${ZeroMQ_LIBRARIES}) +if(BUILD_SIMULITH_TESTS) + # When building tests, expose internal symbols for test-only helpers + target_compile_definitions(simulith PRIVATE SIMULITH_TESTING) +endif() + # Build 42 as a library set(FORTYTWO_SOURCES 42/Source/42exec.c diff --git a/Makefile b/Makefile index fffb188..1cac41c 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,8 @@ build-test: $(MAKE) --no-print-directory -C $(BUILDDIR) cd $(BUILDDIR) && ctest --output-on-failure -O ctest.log lcov -c --directory . --output-file $(BUILDDIR)/coverage.info - genhtml $(BUILDDIR)/coverage.info --output-directory $(BUILDDIR)/report + lcov --remove $(BUILDDIR)/coverage.info '*/test/*' '*/tests/*' --output-file $(BUILDDIR)/coverage.filtered.info + genhtml $(BUILDDIR)/coverage.filtered.info --output-directory $(BUILDDIR)/report @echo "" @echo "Review coverage report: " @echo " firefox $(BUILDDIR)/report/index.html" diff --git a/include/simulith.h b/include/simulith.h index c19c8e8..57a7ecf 100644 --- a/include/simulith.h +++ b/include/simulith.h @@ -41,6 +41,12 @@ extern "C" // Logging function void simulith_log(const char *fmt, ...); +#ifdef SIMULITH_TESTING + /* Test-only helper to reset logging state between tests. Only available + * when building tests (SIMULITH_TESTING). */ + void simulith_log_reset_for_tests(void); +#endif + // ---------- Server API ---------- /** diff --git a/src/simulith_common.c b/src/simulith_common.c index da2c160..5ca65b6 100644 --- a/src/simulith_common.c +++ b/src/simulith_common.c @@ -2,6 +2,7 @@ #include #include #include +#include typedef enum { LOG_MODE_STDOUT, @@ -10,9 +11,18 @@ typedef enum { LOG_MODE_NONE } simulith_log_mode_t; +/* When building tests we make these globals non-static so test code can + * inspect/reset them. In production builds they remain translation-unit + * static to preserve encapsulation. */ +#ifdef SIMULITH_TESTING +simulith_log_mode_t simulith_log_mode = LOG_MODE_STDOUT; +int simulith_log_mode_initialized = 0; +FILE *simulith_log_file = NULL; +#else static simulith_log_mode_t simulith_log_mode = LOG_MODE_STDOUT; static int simulith_log_mode_initialized = 0; static FILE *simulith_log_file = NULL; +#endif static void simulith_log_init_mode(void) { const char *env = getenv("SIMULITH_LOG_MODE"); @@ -72,4 +82,19 @@ void simulith_log(const char *fmt, ...) } else { va_end(args); } -} \ No newline at end of file +} + +#ifdef SIMULITH_TESTING +/* Test-only helper: reset logging to uninitialized state and close any open + * log file. Tests should call this between cases to avoid cross-test + * interference. */ +void simulith_log_reset_for_tests(void) +{ + if (simulith_log_file) { + fclose(simulith_log_file); + simulith_log_file = NULL; + } + simulith_log_mode_initialized = 0; + simulith_log_mode = LOG_MODE_STDOUT; +} +#endif \ No newline at end of file diff --git a/src/simulith_server.c b/src/simulith_server.c index be3c3e7..38a3009 100644 --- a/src/simulith_server.c +++ b/src/simulith_server.c @@ -1,5 +1,6 @@ #include "simulith.h" #include +#include #define MAX_CLIENTS 32 @@ -17,6 +18,9 @@ static uint64_t tick_interval_ns = 0; static int expected_clients = 0; static ClientState client_states[MAX_CLIENTS] = {0}; +/* Test/debug helper: request server shutdown from other threads. */ +static volatile sig_atomic_t simulith_server_stop_requested = 0; + static int is_client_id_taken(const char *id) { for (int i = 0; i < MAX_CLIENTS; ++i) @@ -32,6 +36,9 @@ static int is_client_id_taken(const char *id) int simulith_server_init(const char *pub_bind, const char *rep_bind, int client_count, uint64_t interval_ns) { + /* Clear any previous stop request so a fresh server run isn't short-circuited. */ + simulith_server_stop_requested = 0; + // Validate parameters if (client_count <= 0 || client_count > MAX_CLIENTS) { @@ -75,6 +82,12 @@ int simulith_server_init(const char *pub_bind, const char *rep_bind, int client_ return -1; } + /* Set a short receive timeout on the responder so the server loop can + * periodically check for shutdown requests and avoid getting stuck in a + * blocking recv during tests. */ + int recv_timeout_ms = 200; + zmq_setsockopt(responder, ZMQ_RCVTIMEO, &recv_timeout_ms, sizeof(recv_timeout_ms)); + // Optimize responder settings int rcvhwm = 1000; zmq_setsockopt(responder, ZMQ_RCVHWM, &rcvhwm, sizeof(rcvhwm)); @@ -161,6 +174,11 @@ void simulith_server_run(void) int ready_clients = 0; while (ready_clients < expected_clients) { + if (simulith_server_stop_requested) { + simulith_log("Server shutdown requested while waiting for READY\n"); + return; + } + char buffer[64] = {0}; int size = zmq_recv(responder, buffer, sizeof(buffer) - 1, 0); if (size > 0) @@ -220,6 +238,20 @@ void simulith_server_run(void) zmq_send(responder, "ACK", 3, 0); simulith_log("Registered client %s (%d/%d)\n", client_id, ready_clients, expected_clients); } + else + { + /* recv timed out or failed; check for shutdown and continue waiting */ + if (errno == EAGAIN) + { + /* timeout - loop again so we can detect shutdown requests */ + continue; + } + else + { + simulith_log("Error receiving handshake: %s\n", strerror(errno)); + continue; + } + } } simulith_log("All clients ready. Starting time broadcast.\n"); @@ -403,6 +435,9 @@ void simulith_server_run(void) void simulith_server_shutdown(void) { + /* Request the server loop to stop, then proceed to close sockets. */ + simulith_server_stop_requested = 1; + if (publisher) zmq_close(publisher); if (responder) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 15cdf20..e01ca8b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,12 +9,27 @@ include_directories( ${ZeroMQ_INCLUDE_DIRS} ) -# Main simulith tests executable +add_executable(test_42_command_api test_42.c ${UNITY_SRC}) +target_link_libraries(test_42_command_api simulith ${ZeroMQ_LIBRARIES} pthread) +target_compile_definitions(test_42_command_api PRIVATE SIMULITH_TESTING) +add_test(NAME 42Tests COMMAND test_42_command_api) + +add_executable(test_common test_common.c ${UNITY_SRC}) +target_link_libraries(test_common simulith ${ZeroMQ_LIBRARIES}) +target_compile_definitions(test_common PRIVATE SIMULITH_TESTING) +add_test(NAME CommonTests COMMAND test_common) + add_executable(test_simulith test_simulith.c ${UNITY_SRC}) target_link_libraries(test_simulith simulith ${ZeroMQ_LIBRARIES} pthread) +target_compile_definitions(test_simulith PRIVATE SIMULITH_TESTING) add_test(NAME SimulithTests COMMAND test_simulith) -# Transport tests executable +add_executable(test_time test_time.c ${UNITY_SRC}) +target_link_libraries(test_time simulith ${ZeroMQ_LIBRARIES} pthread) +target_compile_definitions(test_time PRIVATE SIMULITH_TESTING) +add_test(NAME TimeTests COMMAND test_time) + add_executable(test_transport test_transport.c ${UNITY_SRC}) target_link_libraries(test_transport simulith ${ZeroMQ_LIBRARIES}) +target_compile_definitions(test_transport PRIVATE SIMULITH_TESTING) add_test(NAME TransportTests COMMAND test_transport) diff --git a/test/test_42.c b/test/test_42.c new file mode 100644 index 0000000..8a628e2 --- /dev/null +++ b/test/test_42.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include "simulith_transport.h" +#include "unity.h" +#include "simulith_42_commands.h" + +static transport_port_t transport_a_ports[8]; +static transport_port_t transport_b_ports[8]; + +void setUp(void) +{ + memset(transport_a_ports, 0, sizeof(transport_a_ports)); + memset(transport_b_ports, 0, sizeof(transport_b_ports)); +} + +void tearDown(void) +{ + for (int i = 0; i < 8; i++) { + simulith_transport_close(&transport_a_ports[i]); + simulith_transport_close(&transport_b_ports[i]); + } +} + +static void test_enqueue_dequeue_basic(void) +{ + simulith_42_command_t cmd_in = {0}; + simulith_42_command_t cmd_out = {0}; + int rc; + + cmd_in.type = SIMULITH_42_CMD_MTB_TORQUE; + cmd_in.spacecraft_id = 3; + cmd_in.valid = 1; + cmd_in.cmd.mtb.dipole[0] = 1.1; + cmd_in.cmd.mtb.dipole[1] = 2.2; + cmd_in.cmd.mtb.dipole[2] = 3.3; + cmd_in.cmd.mtb.enable_mask = 0x5; + + rc = enqueue_command(&cmd_in); + TEST_ASSERT_EQUAL_INT(0, rc); + + rc = dequeue_command(&cmd_out); + TEST_ASSERT_EQUAL_INT(0, rc); + + TEST_ASSERT_EQUAL_INT(SIMULITH_42_CMD_MTB_TORQUE, cmd_out.type); + TEST_ASSERT_EQUAL_INT(3, cmd_out.spacecraft_id); + TEST_ASSERT_EQUAL_INT(1, cmd_out.valid); + TEST_ASSERT_EQUAL_INT(0x5, cmd_out.cmd.mtb.enable_mask); + TEST_ASSERT_EQUAL_FLOAT((float)1.1, (float)cmd_out.cmd.mtb.dipole[0]); + TEST_ASSERT_EQUAL_FLOAT((float)2.2, (float)cmd_out.cmd.mtb.dipole[1]); + TEST_ASSERT_EQUAL_FLOAT((float)3.3, (float)cmd_out.cmd.mtb.dipole[2]); +} + +static void test_queue_overflow(void) +{ + // Fill the queue completely, then one more should fail + simulith_42_command_t tmp = {0}; + int i; + int rc; + + tmp.type = SIMULITH_42_CMD_NONE; + tmp.valid = 0; + + for (i = 0; i < SIMULITH_42_CMD_QUEUE_SIZE; ++i) { + rc = enqueue_command(&tmp); + TEST_ASSERT_EQUAL_INT(0, rc); + } + // queue now full + rc = enqueue_command(&tmp); + TEST_ASSERT_EQUAL_INT(-1, rc); + + // drain queue + for (i = 0; i < SIMULITH_42_CMD_QUEUE_SIZE; ++i) { + rc = dequeue_command(&tmp); + TEST_ASSERT_EQUAL_INT(0, rc); + } + // now empty + rc = dequeue_command(&tmp); + TEST_ASSERT_EQUAL_INT(-1, rc); +} + +static void test_helper_wrappers(void) +{ + double dipole[3] = {0.1, 0.2, 0.3}; + double torque4[4] = {1.0, 2.0, 3.0, 4.0}; + double thrust[3] = {0.4, 0.5, 0.6}; + double ttorque[3] = {0.7, 0.8, 0.9}; + int rc; + simulith_42_command_t out = {0}; + + rc = simulith_42_send_mtb_command(1, dipole, 0x3); + TEST_ASSERT_EQUAL_INT(0, rc); + + rc = dequeue_command(&out); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_INT(SIMULITH_42_CMD_MTB_TORQUE, out.type); + TEST_ASSERT_EQUAL_INT(1, out.spacecraft_id); + TEST_ASSERT_EQUAL_FLOAT((float)dipole[0], (float)out.cmd.mtb.dipole[0]); + + rc = simulith_42_send_wheel_command(2, torque4, 0xF); + TEST_ASSERT_EQUAL_INT(0, rc); + rc = dequeue_command(&out); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_INT(SIMULITH_42_CMD_WHEEL_TORQUE, out.type); + TEST_ASSERT_EQUAL_INT(2, out.spacecraft_id); + TEST_ASSERT_EQUAL_FLOAT((float)torque4[3], (float)out.cmd.wheel.torque[3]); + + rc = simulith_42_send_thruster_command(4, thrust, ttorque, 0x1); + TEST_ASSERT_EQUAL_INT(0, rc); + rc = dequeue_command(&out); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_INT(SIMULITH_42_CMD_THRUSTER, out.type); + TEST_ASSERT_EQUAL_INT(4, out.spacecraft_id); + TEST_ASSERT_EQUAL_FLOAT((float)ttorque[2], (float)out.cmd.thruster.torque[2]); +} + +static void test_set_mode_extra_and_defaults(void) +{ + // Test sending set_mode with NULL extra (defaults) + int rc; + simulith_42_command_t out = {0}; + + rc = simulith_42_send_set_mode(7, 42, NULL); + TEST_ASSERT_EQUAL_INT(0, rc); + rc = dequeue_command(&out); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_INT(SIMULITH_42_CMD_SET_MODE, out.type); + TEST_ASSERT_EQUAL_INT(7, out.spacecraft_id); + TEST_ASSERT_EQUAL_INT(42, out.cmd.setmode.mode); + TEST_ASSERT_EQUAL_INT(0, out.cmd.setmode.have_pri); + TEST_ASSERT_EQUAL_INT(0, out.cmd.setmode.have_sec); + TEST_ASSERT_EQUAL_INT(0, out.cmd.setmode.have_qrn); + + // Now craft an extra that sets pri and qrn flags and values + simulith_42_command_t extra = {0}; + extra.cmd.setmode.mode = 99; // should be overridden by explicit mode argument + extra.cmd.setmode.have_pri = 1; + extra.cmd.setmode.have_qrn = 1; + extra.cmd.setmode.priW[0] = 9.9; + extra.cmd.setmode.qrn[0] = 1.0; + + rc = simulith_42_send_set_mode(8, 24, &extra.cmd.setmode); + TEST_ASSERT_EQUAL_INT(0, rc); + rc = dequeue_command(&out); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_INT(SIMULITH_42_CMD_SET_MODE, out.type); + TEST_ASSERT_EQUAL_INT(8, out.spacecraft_id); + TEST_ASSERT_EQUAL_INT(24, out.cmd.setmode.mode); // mode should be set to argument + TEST_ASSERT_EQUAL_INT(1, out.cmd.setmode.have_pri); + TEST_ASSERT_EQUAL_INT(1, out.cmd.setmode.have_qrn); + TEST_ASSERT_EQUAL_FLOAT((float)9.9, (float)out.cmd.setmode.priW[0]); + TEST_ASSERT_EQUAL_FLOAT((float)1.0, (float)out.cmd.setmode.qrn[0]); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_enqueue_dequeue_basic); + RUN_TEST(test_queue_overflow); + RUN_TEST(test_helper_wrappers); + RUN_TEST(test_set_mode_extra_and_defaults); + return UNITY_END(); +} diff --git a/test/test_common.c b/test/test_common.c new file mode 100644 index 0000000..f5fd511 --- /dev/null +++ b/test/test_common.c @@ -0,0 +1,147 @@ +#include "unity.h" +#include +#include +#include +#include + +#include "simulith.h" + +// Test-only helper: reset internals by accessing module globals directly. +// When building tests we define SIMULITH_TESTING which makes these symbols +// non-static in the implementation so they are visible here. +extern int simulith_log_mode_initialized; +extern int simulith_log_mode; /* enum type compatible with int */ +extern FILE *simulith_log_file; + +void simulith_log_reset_for_tests(void) +{ + if (simulith_log_file) { + fclose(simulith_log_file); + simulith_log_file = NULL; + } + simulith_log_mode_initialized = 0; + simulith_log_mode = 0; // LOG_MODE_STDOUT +} + +void setUp(void) { } +void tearDown(void) { } + +// Helper: capture stdout output of a function by redirecting to a temporary file +static char* capture_stdout_of(void (*fn)(const char*)) +{ + char tmpl[] = "/tmp/simulith_test_stdout_XXXXXX"; + int fd = mkstemp(tmpl); + TEST_ASSERT_TRUE(fd >= 0); + + // Save stdout + int saved_stdout = dup(STDOUT_FILENO); + TEST_ASSERT_TRUE(saved_stdout >= 0); + + // Redirect stdout to temp file + fflush(stdout); + dup2(fd, STDOUT_FILENO); + + // Call function + fn("hello-from-test\n"); + + // Restore stdout + fflush(stdout); + dup2(saved_stdout, STDOUT_FILENO); + close(saved_stdout); + + // Read file contents + lseek(fd, 0, SEEK_SET); + off_t sz = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + char *buf = malloc(sz + 1); + TEST_ASSERT_NOT_NULL(buf); + ssize_t r = read(fd, buf, sz); + (void)r; + buf[sz] = '\0'; + close(fd); + + return buf; // caller should free +} + +// Adapter to call simulith_log with one string +static void call_log(const char* s) +{ + (void)s; // simulith_log uses printf-style, but we'll pass a constant + simulith_log("%s", "hello-from-test\n"); +} + +static void test_log_default_stdout(void) +{ + // Ensure env unset + unsetenv("SIMULITH_LOG_MODE"); + + char *out = capture_stdout_of(call_log); + TEST_ASSERT_NOT_NULL(out); + TEST_ASSERT_TRUE(strstr(out, "hello-from-test") != NULL); + free(out); +} + +static void test_log_none(void) +{ + setenv("SIMULITH_LOG_MODE", "none", 1); + simulith_log_reset_for_tests(); + char *out = capture_stdout_of(call_log); + TEST_ASSERT_NOT_NULL(out); + TEST_ASSERT_TRUE(strstr(out, "hello-from-test") == NULL); + free(out); + unsetenv("SIMULITH_LOG_MODE"); +} + +static void test_log_file_and_both(void) +{ + const char *logpath = "/tmp/simulith.log"; + // ensure file removed + unlink(logpath); + + // file only + setenv("SIMULITH_LOG_MODE", "file", 1); + simulith_log_reset_for_tests(); + simulith_log("%s", "file-only\n"); + // Give the logging subsystem a moment to open/write the file + fflush(NULL); + usleep(1000); + + FILE *f = fopen(logpath, "r"); + TEST_ASSERT_NOT_NULL(f); + char buf[128]; + bool found = false; + while (fgets(buf, sizeof(buf), f)) { + if (strstr(buf, "file-only")) { found = true; break; } + } + fclose(f); + TEST_ASSERT_TRUE(found); + + // both + unlink(logpath); + setenv("SIMULITH_LOG_MODE", "both", 1); + simulith_log_reset_for_tests(); + char *out = capture_stdout_of(call_log); + TEST_ASSERT_NOT_NULL(out); + TEST_ASSERT_TRUE(strstr(out, "hello-from-test") != NULL); + free(out); + + f = fopen(logpath, "r"); + TEST_ASSERT_NOT_NULL(f); + found = false; + while (fgets(buf, sizeof(buf), f)) { + if (strstr(buf, "hello-from-test")) { found = true; break; } + } + fclose(f); + TEST_ASSERT_TRUE(found); + + unsetenv("SIMULITH_LOG_MODE"); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_log_default_stdout); + RUN_TEST(test_log_none); + RUN_TEST(test_log_file_and_both); + return UNITY_END(); +} diff --git a/test/test_simulith.c b/test/test_simulith.c index 9c2e20e..9fc018b 100644 --- a/test/test_simulith.c +++ b/test/test_simulith.c @@ -2,7 +2,11 @@ #include "unity.h" #include #include +#include +#include #include +#include +#include #define INVALID_ADDR "invalid://address" #define CLIENT_ID "test_client" @@ -32,6 +36,47 @@ static void *server_thread(void *arg) return NULL; } +// Server thread that accepts expected client count via malloc'd int pointer +static void *server_thread_with_clients(void *arg) +{ + int expected = 1; + if (arg) + { + int *p = (int *)arg; + expected = *p; + free(p); + } + + simulith_server_init(LOCAL_PUB_ADDR, LOCAL_REP_ADDR, expected, INTERVAL_NS); + simulith_server_run(); + return NULL; +} + +// Helper: send a raw REQ message to addr and receive reply (timeouted). Returns 0 on success. +static int zmq_req_send_and_recv(const char *addr, const char *msg, char *reply, size_t reply_len) +{ + void *ctx = zmq_ctx_new(); + if (!ctx) return -1; + void *req = zmq_socket(ctx, ZMQ_REQ); + if (!req) { zmq_ctx_term(ctx); return -1; } + if (zmq_connect(req, addr) != 0) { zmq_close(req); zmq_ctx_term(ctx); return -1; } + + int timeout_ms = 2000; + zmq_setsockopt(req, ZMQ_RCVTIMEO, &timeout_ms, sizeof(timeout_ms)); + + if (zmq_send(req, msg, strlen(msg), 0) == -1) { zmq_close(req); zmq_ctx_term(ctx); return -1; } + + char buf[128] = {0}; + int rc = zmq_recv(req, buf, sizeof(buf) - 1, 0); + if (rc < 0) { zmq_close(req); zmq_ctx_term(ctx); return -1; } + buf[rc] = '\0'; + if (reply && reply_len > 0) strncpy(reply, buf, reply_len - 1); + + zmq_close(req); + zmq_ctx_term(ctx); + return 0; +} + static void *client_thread(void *arg) { usleep(1000); // Wait for server to be ready @@ -128,6 +173,114 @@ static void test_client_handshake_no_server(void) simulith_client_shutdown(); } +// Server should reply ERR to malformed handshake messages +static void test_server_handshake_invalid_format(void) +{ + pthread_t server; + int *p = malloc(sizeof(int)); *p = 1; + pthread_create(&server, NULL, server_thread_with_clients, p); + usleep(10000); + + char reply[128] = {0}; + int rc = zmq_req_send_and_recv(LOCAL_REP_ADDR, "BADMSG", reply, sizeof(reply)); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_STRING("ERR", reply); + + simulith_server_shutdown(); + pthread_cancel(server); + pthread_join(server, NULL); +} + +// Duplicate READY messages with same client id should result in DUP_ID on the second request +static void test_server_handshake_duplicate_client_id(void) +{ + pthread_t server; + int *p = malloc(sizeof(int)); *p = 2; // expect two clients + pthread_create(&server, NULL, server_thread_with_clients, p); + usleep(10000); + + // Use the raw ZMQ helper to perform two READY messages which should result + // in ACK then DUP_ID. This avoids the library's single-client global state. + usleep(20000); // give server more time to bind + char r1[128] = {0}; + char r2[128] = {0}; + int rc1 = zmq_req_send_and_recv(LOCAL_REP_ADDR, "READY DUPTEST", r1, sizeof(r1)); + TEST_ASSERT_EQUAL_INT(0, rc1); + TEST_ASSERT_EQUAL_STRING("ACK", r1); + + usleep(10000); + + int rc2 = zmq_req_send_and_recv(LOCAL_REP_ADDR, "READY DUPTEST", r2, sizeof(r2)); + TEST_ASSERT_EQUAL_INT(0, rc2); + TEST_ASSERT_EQUAL_STRING("DUP_ID", r2); + + simulith_server_shutdown(); + pthread_cancel(server); + pthread_join(server, NULL); +} + +// After a proper READY/ACK handshake, sending the client id as an ACK should elicit an "ACK" reply +static void test_server_ack_handling(void) +{ + pthread_t server; + int *p = malloc(sizeof(int)); *p = 1; + pthread_create(&server, NULL, server_thread_with_clients, p); + usleep(10000); + + usleep(20000); // allow server to bind and start broadcasting + + char reply[128] = {0}; + int rc = zmq_req_send_and_recv(LOCAL_REP_ADDR, "READY ACKTEST", reply, sizeof(reply)); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_STRING("ACK", reply); + + usleep(20000); + + int rc2 = zmq_req_send_and_recv(LOCAL_REP_ADDR, "ACKTEST", reply, sizeof(reply)); + TEST_ASSERT_EQUAL_INT(0, rc2); + TEST_ASSERT_EQUAL_STRING("ACK", reply); + + simulith_server_shutdown(); + pthread_cancel(server); + pthread_join(server, NULL); +} + +// Test server CLI: send a sequence of commands via a pipe to stdin to trigger pause/play and speed changes +static void test_server_cli_commands(void) +{ + // Start server in a thread and then send commands to its stdin via popen of the standalone + pid_t pid = fork(); + if (pid == 0) { + // Child: exec the standalone server with 1 client + execlp("./build/simulith_server_standalone", "simulith_server_standalone", "1", (char *)NULL); + _exit(127); + } + + // Parent: allow server to start, then write commands to its stdin via /proc//fd/0 is not writable + // Instead we sleep a bit and then kill to trigger clean shutdown path (this exercises startup and shutdown) + sleep(1); + kill(pid, SIGTERM); + int status = 0; + waitpid(pid, &status, 0); + TEST_ASSERT_TRUE(WIFEXITED(status) || WIFSIGNALED(status)); +} + +// Test that the standalone server exits with non-zero on invalid arg +static void test_server_standalone_invalid_arg(void) +{ + pid_t pid = fork(); + if (pid == 0) { + execlp("./build/simulith_server_standalone", "simulith_server_standalone", "0", (char *)NULL); + _exit(127); + } + int status = 0; + waitpid(pid, &status, 0); + // The standalone should exit non-zero for invalid client count + TEST_ASSERT_TRUE(WIFEXITED(status)); + int exit_code = WEXITSTATUS(status); + TEST_ASSERT_NOT_EQUAL(0, exit_code); +} + int main(void) { UNITY_BEGIN(); @@ -139,6 +292,13 @@ int main(void) RUN_TEST(test_client_init_invalid_params); RUN_TEST(test_client_handshake_no_server); + RUN_TEST(test_server_handshake_invalid_format); + RUN_TEST(test_server_handshake_duplicate_client_id); + RUN_TEST(test_server_ack_handling); + + RUN_TEST(test_server_cli_commands); + RUN_TEST(test_server_standalone_invalid_arg); + // TODO: test_duplicate_client_ids return UNITY_END(); diff --git a/test/test_time.c b/test/test_time.c new file mode 100644 index 0000000..1fd102c --- /dev/null +++ b/test/test_time.c @@ -0,0 +1,55 @@ +#include "unity.h" +#include +#include +#include +#include + +#include "simulith_time.h" +#include "simulith.h" + +void setUp(void) { } +void tearDown(void) { } + +static void test_time_init_get_wait_cleanup(void) +{ + // Create a PUB socket to send a tick to LOCAL_PUB_ADDR + void* ctx = zmq_ctx_new(); + TEST_ASSERT_NOT_NULL(ctx); + + void* pub = zmq_socket(ctx, ZMQ_PUB); + TEST_ASSERT_NOT_NULL(pub); + + int rc = zmq_bind(pub, LOCAL_PUB_ADDR); + TEST_ASSERT_EQUAL_INT(0, rc); + + // Initialize the time provider which connects to LOCAL_PUB_ADDR + void* handle = simulith_time_init(); + TEST_ASSERT_NOT_NULL(handle); + + // Send a tick (uint64_t) through the PUB socket + uint64_t tick = 1; + // Sleep briefly to allow subscriber to connect + usleep(1000); + + int s = zmq_send(pub, &tick, sizeof(tick), 0); + TEST_ASSERT_EQUAL_INT(sizeof(tick), s); + + // Wait for next tick via provider + rc = simulith_time_wait_for_next_tick(handle); + TEST_ASSERT_EQUAL_INT(0, rc); + + double t = simulith_time_get(handle); + TEST_ASSERT_TRUE(t >= 0.0); + + simulith_time_cleanup(handle); + + zmq_close(pub); + zmq_ctx_destroy(ctx); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_time_init_get_wait_cleanup); + return UNITY_END(); +} From b3e0a820a55cf86d274920d73012f4af9e0dbace Mon Sep 17 00:00:00 2001 From: "Lucas, John P." Date: Thu, 18 Sep 2025 07:43:07 -0400 Subject: [PATCH 4/6] [tryspaceorg/tryspace-lab#55] Added test for remaining missed function; --- test/test_simulith.c | 71 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/test/test_simulith.c b/test/test_simulith.c index 9fc018b..86a4aa7 100644 --- a/test/test_simulith.c +++ b/test/test_simulith.c @@ -173,6 +173,31 @@ static void test_client_handshake_no_server(void) simulith_client_shutdown(); } +// Test blocking wait-for-tick API: server should send a tick and client should receive it +static void test_client_wait_for_tick(void) +{ + pthread_t server; + int *p = malloc(sizeof(int)); *p = 1; + pthread_create(&server, NULL, server_thread_with_clients, p); + usleep(10000); // give server time to bind and start + + int rc = simulith_client_init(LOCAL_PUB_ADDR, LOCAL_REP_ADDR, CLIENT_ID, INTERVAL_NS); + TEST_ASSERT_EQUAL_INT(0, rc); + + rc = simulith_client_handshake(); + TEST_ASSERT_EQUAL_INT(0, rc); + + uint64_t tick_ns = 0; + rc = simulith_client_wait_for_tick(&tick_ns); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_GREATER_THAN(0, tick_ns); + + simulith_client_shutdown(); + simulith_server_shutdown(); + pthread_cancel(server); + pthread_join(server, NULL); +} + // Server should reply ERR to malformed handshake messages static void test_server_handshake_invalid_format(void) { @@ -281,6 +306,47 @@ static void test_server_standalone_invalid_arg(void) TEST_ASSERT_NOT_EQUAL(0, exit_code); } +// Sending an ACK with an unknown client id should log an "ACK received from unknown client" message +static void test_server_handle_unknown_client_ack(void) +{ + pthread_t server; + int *p = malloc(sizeof(int)); *p = 1; + // Log to file so we can inspect the message + setenv("SIMULITH_LOG_MODE", "file", 1); + simulith_log_reset_for_tests(); + pthread_create(&server, NULL, server_thread_with_clients, p); + usleep(10000); + + char reply[128] = {0}; + int rc = zmq_req_send_and_recv(LOCAL_REP_ADDR, "READY KNOWN", reply, sizeof(reply)); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_STRING("ACK", reply); + + usleep(20000); + + rc = zmq_req_send_and_recv(LOCAL_REP_ADDR, "UNKNOWN123", reply, sizeof(reply)); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_EQUAL_STRING("ACK", reply); + + // Give logger a moment to flush to file + usleep(10000); + + FILE *f = fopen("/tmp/simulith.log", "r"); + TEST_ASSERT_NOT_NULL(f); + char buf[256]; + int found = 0; + while (fgets(buf, sizeof(buf), f)) { + if (strstr(buf, "ACK received from unknown client: UNKNOWN123")) { found = 1; break; } + } + fclose(f); + TEST_ASSERT_TRUE(found); + + simulith_server_shutdown(); + pthread_cancel(server); + pthread_join(server, NULL); + unsetenv("SIMULITH_LOG_MODE"); +} + int main(void) { UNITY_BEGIN(); @@ -291,15 +357,14 @@ int main(void) RUN_TEST(test_client_init_invalid_address); RUN_TEST(test_client_init_invalid_params); RUN_TEST(test_client_handshake_no_server); + RUN_TEST(test_client_wait_for_tick); RUN_TEST(test_server_handshake_invalid_format); RUN_TEST(test_server_handshake_duplicate_client_id); RUN_TEST(test_server_ack_handling); - RUN_TEST(test_server_cli_commands); RUN_TEST(test_server_standalone_invalid_arg); - - // TODO: test_duplicate_client_ids + RUN_TEST(test_server_handle_unknown_client_ack); return UNITY_END(); } From defecb79c011af4990dfd327afbb014fd1142022 Mon Sep 17 00:00:00 2001 From: "Lucas, John P." Date: Thu, 18 Sep 2025 08:00:13 -0400 Subject: [PATCH 5/6] [tryspaceorg/tryspace-lab#55] Updated flags when CompFlags unavailable and added codecov action to CI; --- .github/workflows/ci.yml | 4 ++++ CMakeLists.txt | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a12ac1..e3b08ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,3 +36,7 @@ jobs: - name: Build Test run: | make build-test + - name: Upload to Codecov + uses: codecov/codecov-action@v5 + with: + verbose: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 807d4a5..744b407 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,21 @@ if(EXISTS "${COMP_FLAGS_FILE}") message(STATUS "Including component flags from ${COMP_FLAGS_FILE}") include("${COMP_FLAGS_FILE}") elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + # Basic fallback flags when component flags aren't available add_compile_options(-Wall -Werror) + # If building tests, ensure we enable diagnostics and coverage flags + if(BUILD_SIMULITH_TESTS) + message(STATUS "CompFlags.cmake not found — adding test/coverage flags for GNU/Clang") + add_compile_options(-fdiagnostics-show-option -fprofile-arcs -ftest-coverage) + # Ensure linker emits coverage data too + if(POLICY CMP0068) + # add_link_options is available; attach --coverage for linking + add_link_options(--coverage) + else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") + endif() + endif() endif() if(BUILD_SIMULITH_TESTS) From 2e9918a73207371068a1882567d2e8a15c076507 Mon Sep 17 00:00:00 2001 From: "Lucas, John P." Date: Thu, 18 Sep 2025 08:08:38 -0400 Subject: [PATCH 6/6] [tryspaceorg/tryspace-lab#55] Updates after code review; --- src/simulith_server.c | 6 +++--- test/test_simulith.c | 16 ++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/simulith_server.c b/src/simulith_server.c index 38a3009..da70fba 100644 --- a/src/simulith_server.c +++ b/src/simulith_server.c @@ -121,9 +121,9 @@ static void broadcast_time(void) // Calculate actual speed (sim seconds per real second) struct timespec now_ts; clock_gettime(CLOCK_MONOTONIC, &now_ts); - uint64_t now_real_ns = (uint64_t)now_ts.tv_sec * 1000000000ULL + (uint64_t)now_ts.tv_nsec; - double sim_elapsed = (double)(current_time_ns - last_log_time) / 1e9; - double real_elapsed = (g_last_log_real_ns > 0) ? ((double)(now_real_ns - g_last_log_real_ns) / 1e9) : 0.0; + uint64_t now_real_ns = (uint64_t)now_ts.tv_sec * 1000000000ULL + (uint64_t)now_ts.tv_nsec; + double sim_elapsed = (double)(current_time_ns - last_log_time) / 1e9; + double real_elapsed = (g_last_log_real_ns > 0) ? ((double)(now_real_ns - g_last_log_real_ns) / 1e9) : 0.0; double actual_speed = (real_elapsed > 0.0) ? (sim_elapsed / real_elapsed) : 0.0; simulith_log(" Simulation time: %.3f seconds | Attempted speed: %.2fx | Actual: %.2fx\n", diff --git a/test/test_simulith.c b/test/test_simulith.c index 86a4aa7..8990112 100644 --- a/test/test_simulith.c +++ b/test/test_simulith.c @@ -44,7 +44,6 @@ static void *server_thread_with_clients(void *arg) { int *p = (int *)arg; expected = *p; - free(p); } simulith_server_init(LOCAL_PUB_ADDR, LOCAL_REP_ADDR, expected, INTERVAL_NS); @@ -177,7 +176,8 @@ static void test_client_handshake_no_server(void) static void test_client_wait_for_tick(void) { pthread_t server; - int *p = malloc(sizeof(int)); *p = 1; + int i = 1; + int *p = &i; pthread_create(&server, NULL, server_thread_with_clients, p); usleep(10000); // give server time to bind and start @@ -202,7 +202,8 @@ static void test_client_wait_for_tick(void) static void test_server_handshake_invalid_format(void) { pthread_t server; - int *p = malloc(sizeof(int)); *p = 1; + int i = 1; + int *p = &i; pthread_create(&server, NULL, server_thread_with_clients, p); usleep(10000); @@ -220,7 +221,8 @@ static void test_server_handshake_invalid_format(void) static void test_server_handshake_duplicate_client_id(void) { pthread_t server; - int *p = malloc(sizeof(int)); *p = 2; // expect two clients + int i = 2; // two clients expected + int *p = &i; pthread_create(&server, NULL, server_thread_with_clients, p); usleep(10000); @@ -248,7 +250,8 @@ static void test_server_handshake_duplicate_client_id(void) static void test_server_ack_handling(void) { pthread_t server; - int *p = malloc(sizeof(int)); *p = 1; + int i = 1; + int *p = &i; pthread_create(&server, NULL, server_thread_with_clients, p); usleep(10000); @@ -310,7 +313,8 @@ static void test_server_standalone_invalid_arg(void) static void test_server_handle_unknown_client_ack(void) { pthread_t server; - int *p = malloc(sizeof(int)); *p = 1; + int i = 1; + int *p = &i; // Log to file so we can inspect the message setenv("SIMULITH_LOG_MODE", "file", 1); simulith_log_reset_for_tests();