diff --git a/CMakeLists.txt b/CMakeLists.txt index 60b49e3..b17a2a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,10 +57,10 @@ CPMAddPackage( "gtest_force_shared_crt ON" ) -file( - GLOB_RECURSE LIB_SOURCES - "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" -) +# Library sources: src/*.cpp only, exclude *.test.cpp and test_helpers/ +file(GLOB_RECURSE LIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +list(FILTER LIB_SOURCES EXCLUDE REGEX ".*\\.test\\.cpp$") +list(FILTER LIB_SOURCES EXCLUDE REGEX ".*/test_helpers/.*") add_library(cpp_bindings_linux SHARED ${LIB_SOURCES}) @@ -88,11 +88,11 @@ target_link_libraries( target_compile_features(cpp_bindings_linux PUBLIC cxx_std_23) -# Collect all test source files -file( - GLOB_RECURSE TEST_SOURCES - "${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cpp" -) +# Test sources: src/*.test.cpp, tests/*.test.cpp, src/test_helpers/*.cpp (helpers excluded from lib) +file(GLOB SRC_UNIT_TESTS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.test.cpp") +file(GLOB TESTS_INTEGRATION "${CMAKE_CURRENT_SOURCE_DIR}/tests/*.test.cpp") +file(GLOB TEST_HELPER_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/test_helpers/*.cpp") +set(TEST_SOURCES ${SRC_UNIT_TESTS} ${TESTS_INTEGRATION} ${TEST_HELPER_SOURCES}) if(TEST_SOURCES) add_executable(cpp_bindings_linux_tests ${TEST_SOURCES}) @@ -101,6 +101,7 @@ if(TEST_SOURCES) cpp_bindings_linux_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/generated ) diff --git a/src/serial_close.test.cpp b/src/serial_close.test.cpp new file mode 100644 index 0000000..5b89b64 --- /dev/null +++ b/src/serial_close.test.cpp @@ -0,0 +1,81 @@ +#include +#include + +#include + +#include + +#include "test_helpers/error_capture.hpp" + +class SerialCloseTest : public ::testing::Test +{ + protected: + void SetUp() override + { + ErrorCapture::instance = &error_capture; + error_callback = &ErrorCapture::callback; + } + + void TearDown() override + { + ErrorCapture::instance = nullptr; + } + + ErrorCapture error_capture; + ErrorCallbackT error_callback = nullptr; +}; + +TEST_F(SerialCloseTest, CloseInvalidHandleZero) +{ + int result = serialClose(0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kSuccess)); +} + +TEST_F(SerialCloseTest, CloseInvalidHandleNegative) +{ + int result = serialClose(-1, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kSuccess)); +} + +TEST_F(SerialCloseTest, CloseInvalidHandleNegativeLarge) +{ + int result = serialClose(-12345, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kSuccess)); +} + +TEST_F(SerialCloseTest, CloseInvalidHandleTooLarge) +{ + auto too_large_handle = static_cast(std::numeric_limits::max()) + 1; + int result = serialClose(too_large_handle, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialCloseTest, CloseInvalidHandleIntMaxBoundary) +{ + auto handle = static_cast(std::numeric_limits::max()); + int result = serialClose(handle, error_callback); + + // Should fail because this fd doesn't exist, but not with InvalidHandleError + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialCloseTest, CloseNoErrorCallback) +{ + int result = serialClose(0, nullptr); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kSuccess)); +} + +TEST_F(SerialCloseTest, CloseInvalidHandle) +{ + // Test closing a real invalid fd (one that never existed) + // We should get an error but not crash + int result = serialClose(9999, error_callback); + + // This will fail because the fd doesn't exist + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kCloseHandleError)); +} diff --git a/src/serial_open.test.cpp b/src/serial_open.test.cpp new file mode 100644 index 0000000..b818ee1 --- /dev/null +++ b/src/serial_open.test.cpp @@ -0,0 +1,209 @@ +#include +#include + +#include +#include + +#include +#include + +#include "test_helpers/error_capture.hpp" + +class SerialOpenTest : public ::testing::Test +{ + protected: + void SetUp() override + { + ErrorCapture::instance = &error_capture; + error_callback = &ErrorCapture::callback; + } + + void TearDown() override + { + ErrorCapture::instance = nullptr; + } + + ErrorCapture error_capture; + ErrorCallbackT error_callback = nullptr; +}; + +TEST_F(SerialOpenTest, NullPortParameter) +{ + intptr_t result = serialOpen(nullptr, 9600, 8, 0, 1, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kNotFoundError)); + EXPECT_NE(error_capture.last_message.find("nullptr"), std::string::npos); +} + +TEST_F(SerialOpenTest, BaudrateTooLow) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 100, 8, 0, 1, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kSetStateError)); + EXPECT_NE(error_capture.last_message.find("baudrate"), std::string::npos); +} + +TEST_F(SerialOpenTest, BaudrateTooLowBoundary) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 299, 8, 0, 1, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, BaudrateBoundaryValid) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 300, 8, 0, 1, error_callback); + + // /dev/null is not a real serial port, but should pass baudrate validation + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, DataBitsTooLow) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 4, 0, 1, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kSetStateError)); + EXPECT_NE(error_capture.last_message.find("data bits"), std::string::npos); +} + +TEST_F(SerialOpenTest, DataBitsTooHigh) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 9, 0, 1, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, ValidDataBits5) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 5, 0, 1, error_callback); + + // Should pass data bits validation + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, ValidDataBits6) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 6, 0, 1, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, ValidDataBits7) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 7, 0, 1, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, ValidDataBits8) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 0, 1, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, InvalidParity) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 5, 1, error_callback); + + // Invalid parity should return an error + EXPECT_LT(result, 0); +} + +TEST_F(SerialOpenTest, ValidParityNone) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 0, 1, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, ValidParityEven) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 1, 1, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, ValidParityOdd) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 2, 1, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, InvalidStopBits) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 0, 3, error_callback); + + // Invalid stop bits should return an error + EXPECT_LT(result, 0); +} + +TEST_F(SerialOpenTest, ValidStopBits0) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 0, 0, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, ValidStopBits1) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 0, 1, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, ValidStopBits2) +{ + const char *port = "/dev/null"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 0, 2, error_callback); + + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)); +} + +TEST_F(SerialOpenTest, NonExistentPort) +{ + const char *port = "/dev/ttyNONEXISTENT99999"; + intptr_t result = serialOpen(const_cast(static_cast(port)), 9600, 8, 0, 1, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kNotFoundError)); +} + +TEST_F(SerialOpenTest, VariousBaudrates) +{ + const char *port = "/dev/null"; + + // Test common baudrates + const std::array baudrates = {300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800}; + + for (int baudrate : baudrates) + { + intptr_t result = + serialOpen(const_cast(static_cast(port)), baudrate, 8, 0, 1, error_callback); + EXPECT_NE(result, static_cast(cpp_core::StatusCodes::kSetStateError)) + << "Baudrate " << baudrate << " should be valid"; + } +} + +TEST_F(SerialOpenTest, NoErrorCallbackNullPort) +{ + intptr_t result = serialOpen(nullptr, 9600, 8, 0, 1, nullptr); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kNotFoundError)); +} diff --git a/src/serial_read.test.cpp b/src/serial_read.test.cpp new file mode 100644 index 0000000..543b6a0 --- /dev/null +++ b/src/serial_read.test.cpp @@ -0,0 +1,127 @@ +#include +#include + +#include +#include +#include +#include + +#include + +#include "test_helpers/error_capture.hpp" + +class SerialReadTest : public ::testing::Test +{ + protected: + void SetUp() override + { + ErrorCapture::instance = &error_capture; + error_callback = &ErrorCapture::callback; + } + + void TearDown() override + { + ErrorCapture::instance = nullptr; + } + + ErrorCapture error_capture; + ErrorCallbackT error_callback = nullptr; +}; + +TEST_F(SerialReadTest, ReadNullBuffer) +{ + int result = serialRead(1, nullptr, 10, 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kBufferError)); + EXPECT_NE(error_capture.last_message.find("buffer"), std::string::npos); +} + +TEST_F(SerialReadTest, ReadZeroBufferSize) +{ + std::array buffer{}; + int result = serialRead(1, buffer.data(), 0, 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kBufferError)); +} + +TEST_F(SerialReadTest, ReadNegativeBufferSize) +{ + std::array buffer{}; + int result = serialRead(1, buffer.data(), -1, 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kBufferError)); +} + +TEST_F(SerialReadTest, ReadInvalidHandleZero) +{ + std::array buffer{}; + int result = serialRead(0, buffer.data(), static_cast(buffer.size()), 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialReadTest, ReadInvalidHandleNegative) +{ + std::array buffer{}; + int result = serialRead(-1, buffer.data(), static_cast(buffer.size()), 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialReadTest, ReadInvalidHandleTooLarge) +{ + std::array buffer{}; + auto too_large = static_cast(std::numeric_limits::max()) + 1; + int result = serialRead(too_large, buffer.data(), static_cast(buffer.size()), 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialReadTest, ReadFromDevNull) +{ + int fd = open("/dev/null", O_RDONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + std::array buffer{}; + int result = serialRead(fd, buffer.data(), static_cast(buffer.size()), 0, 0, error_callback); + + EXPECT_EQ(result, 0); + close(fd); +} + +TEST_F(SerialReadTest, ReadWithLargeBufferSize) +{ + int fd = open("/dev/null", O_RDONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + std::array buffer{}; + int result = serialRead(fd, buffer.data(), static_cast(buffer.size()), 0, 0, error_callback); + + EXPECT_EQ(result, 0); + close(fd); +} + +TEST_F(SerialReadTest, ReadNoErrorCallback) +{ + std::array buffer{}; + int result = serialRead(0, buffer.data(), static_cast(buffer.size()), 100, 0, nullptr); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialReadTest, ReadWithVariousTimeouts) +{ + int fd = open("/dev/null", O_RDONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + std::array buffer{}; + + // Test various timeout values + for (int timeout : {0, 1, 10, 100, 1000}) + { + int result = serialRead(fd, buffer.data(), static_cast(buffer.size()), timeout, 0, error_callback); + EXPECT_EQ(result, 0) << "Timeout " << timeout << " should return 0 for /dev/null"; + } + + close(fd); +} diff --git a/src/serial_write.test.cpp b/src/serial_write.test.cpp new file mode 100644 index 0000000..015211f --- /dev/null +++ b/src/serial_write.test.cpp @@ -0,0 +1,166 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "test_helpers/error_capture.hpp" + +class SerialWriteTest : public ::testing::Test +{ + protected: + void SetUp() override + { + ErrorCapture::instance = &error_capture; + error_callback = &ErrorCapture::callback; + } + + void TearDown() override + { + ErrorCapture::instance = nullptr; + } + + ErrorCapture error_capture; + ErrorCallbackT error_callback = nullptr; +}; + +TEST_F(SerialWriteTest, WriteNullBuffer) +{ + int result = serialWrite(1, nullptr, 10, 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kBufferError)); + EXPECT_NE(error_capture.last_message.find("buffer"), std::string::npos); +} + +TEST_F(SerialWriteTest, WriteZeroBufferSize) +{ + std::array buffer{}; + int result = serialWrite(1, buffer.data(), 0, 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kBufferError)); +} + +TEST_F(SerialWriteTest, WriteNegativeBufferSize) +{ + std::array buffer{}; + int result = serialWrite(1, buffer.data(), -1, 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kBufferError)); +} + +TEST_F(SerialWriteTest, WriteInvalidHandleZero) +{ + const char *buffer = "test"; + int result = serialWrite(0, buffer, static_cast(strlen(buffer)), 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialWriteTest, WriteInvalidHandleNegative) +{ + const char *buffer = "test"; + int result = serialWrite(-1, buffer, static_cast(strlen(buffer)), 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialWriteTest, WriteInvalidHandleTooLarge) +{ + const char *buffer = "test"; + auto too_large = static_cast(std::numeric_limits::max()) + 1; + int result = serialWrite(too_large, buffer, static_cast(strlen(buffer)), 100, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)); +} + +TEST_F(SerialWriteTest, WriteToDevNull) +{ + int fd = open("/dev/null", O_WRONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + const char *test_data = "Hello World"; + const int len = static_cast(strlen(test_data)); + int result = serialWrite(fd, test_data, len, 0, 0, error_callback); + + EXPECT_EQ(result, len); + close(fd); +} + +TEST_F(SerialWriteTest, WriteLargeBuffer) +{ + int fd = open("/dev/null", O_WRONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + std::string large_data(4096, 'A'); + const int len = static_cast(large_data.size()); + int result = serialWrite(fd, large_data.c_str(), len, 0, 0, error_callback); + + EXPECT_EQ(result, len); + close(fd); +} + +TEST_F(SerialWriteTest, WriteMultipleSmallBuffers) +{ + int fd = open("/dev/null", O_WRONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + const char *data = "test"; + const int len = static_cast(strlen(data)); + for (int i = 0; i < 10; ++i) + { + int result = serialWrite(fd, data, len, 0, 0, error_callback); + EXPECT_EQ(result, len); + } + + close(fd); +} + +TEST_F(SerialWriteTest, WriteNoErrorCallback) +{ + int fd = open("/dev/null", O_WRONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + const char *test_data = "test"; + const int len = static_cast(strlen(test_data)); + int result = serialWrite(fd, test_data, len, 0, 0, nullptr); + + EXPECT_EQ(result, len); + close(fd); +} + +TEST_F(SerialWriteTest, WriteWithVariousTimeouts) +{ + int fd = open("/dev/null", O_WRONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + const char *test_data = "test"; + const int len = static_cast(strlen(test_data)); + + // Test various timeout values + for (int timeout : {0, 1, 10, 100, 1000}) + { + int result = serialWrite(fd, test_data, len, timeout, 0, error_callback); + EXPECT_EQ(result, len) << "Timeout " << timeout << " should succeed for /dev/null"; + } + + close(fd); +} + +TEST_F(SerialWriteTest, WriteEmptyStringToDevNull) +{ + int fd = open("/dev/null", O_WRONLY | O_NONBLOCK); + ASSERT_GE(fd, 0); + + const char *empty = ""; + // This should fail because buffer_size is 0 + int result = serialWrite(fd, empty, 0, 0, 0, error_callback); + + EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kBufferError)); + close(fd); +} diff --git a/src/test_helpers/error_capture.cpp b/src/test_helpers/error_capture.cpp new file mode 100644 index 0000000..5b58f09 --- /dev/null +++ b/src/test_helpers/error_capture.cpp @@ -0,0 +1,12 @@ +#include "test_helpers/error_capture.hpp" + +void ErrorCapture::callback(int code, const char *message) +{ + if (instance != nullptr) + { + instance->last_code = code; + instance->last_message = message != nullptr ? message : ""; + } +} + +ErrorCapture *ErrorCapture::instance = nullptr; diff --git a/src/test_helpers/error_capture.hpp b/src/test_helpers/error_capture.hpp new file mode 100644 index 0000000..9749538 --- /dev/null +++ b/src/test_helpers/error_capture.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#include + +struct ErrorCapture +{ + int last_code = 0; + std::string last_message; + + static void callback(int code, const char *message); + + static ErrorCapture *instance; +}; diff --git a/tests/integration.test.cpp b/tests/integration.test.cpp new file mode 100644 index 0000000..99fb133 --- /dev/null +++ b/tests/integration.test.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "test_helpers/error_capture.hpp" + +class SerialIntegrationTest : public ::testing::Test +{ + protected: + void SetUp() override + { + ErrorCapture::instance = &error_capture; + error_callback = &ErrorCapture::callback; + } + + void TearDown() override + { + ErrorCapture::instance = nullptr; + } + + ErrorCapture error_capture; + ErrorCallbackT error_callback = nullptr; +}; + +TEST_F(SerialIntegrationTest, ReadWritePipeRoundTrip) +{ + std::array pipefd{}; + ASSERT_EQ(pipe(pipefd.data()), 0); + + // Set non-blocking + fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + fcntl(pipefd[1], F_SETFL, O_NONBLOCK); + + const char *test_message = "Hello"; + const int msg_len = static_cast(strlen(test_message)); + int write_result = serialWrite(pipefd[1], test_message, msg_len, 100, 0, error_callback); + EXPECT_EQ(write_result, msg_len); + + std::array read_buffer{}; + int read_result = + serialRead(pipefd[0], read_buffer.data(), static_cast(read_buffer.size()), 100, 0, error_callback); + EXPECT_EQ(read_result, msg_len); + EXPECT_EQ(std::string(read_buffer.data()), std::string(test_message)); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST_F(SerialIntegrationTest, MultipleWrites) +{ + std::array pipefd{}; + ASSERT_EQ(pipe(pipefd.data()), 0); + + fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + fcntl(pipefd[1], F_SETFL, O_NONBLOCK); + + const char *msg1 = "Hello"; + const char *msg2 = "World"; + + serialWrite(pipefd[1], msg1, static_cast(strlen(msg1)), 100, 0, error_callback); + serialWrite(pipefd[1], msg2, static_cast(strlen(msg2)), 100, 0, error_callback); + + std::array read_buffer{}; + int read_result = + serialRead(pipefd[0], read_buffer.data(), static_cast(read_buffer.size()), 100, 0, error_callback); + EXPECT_GE(read_result, 0); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST_F(SerialIntegrationTest, CloseAfterOperations) +{ + std::array pipefd{}; + ASSERT_EQ(pipe(pipefd.data()), 0); + + const char *test_data = "test"; + serialWrite(pipefd[1], test_data, static_cast(strlen(test_data)), 100, 0, error_callback); + + std::array buffer{}; + serialRead(pipefd[0], buffer.data(), static_cast(buffer.size()), 100, 0, error_callback); + + int close_result1 = serialClose(pipefd[0], error_callback); + int close_result2 = serialClose(pipefd[1], error_callback); + + EXPECT_EQ(close_result1, static_cast(cpp_core::StatusCodes::kSuccess)); + EXPECT_EQ(close_result2, static_cast(cpp_core::StatusCodes::kSuccess)); +} diff --git a/tests/test_serial_arduino.cpp b/tests/serial_arduino.test.cpp similarity index 57% rename from tests/test_serial_arduino.cpp rename to tests/serial_arduino.test.cpp index 6772c4a..994f175 100644 --- a/tests/test_serial_arduino.cpp +++ b/tests/serial_arduino.test.cpp @@ -1,4 +1,4 @@ -// Test for serial communication with Arduino echo script on /dev/ttyUSB0 +// Integration test: serial communication with Arduino echo script on /dev/ttyUSB0 #include #include @@ -8,18 +8,20 @@ #include #include +#include #include +#include class SerialArduinoTest : public ::testing::Test { protected: void SetUp() override { - const char *env_port = std::getenv("SERIAL_TEST_PORT"); + const char *env_port = std::getenv("SERIAL_TEST_PORT"); // NOLINT(concurrency-mt-unsafe) const char *port = env_port != nullptr ? env_port : "/dev/ttyUSB0"; - handle_ = serialOpen(const_cast(static_cast(port)), 115200, 8, 0, 0, nullptr); + handle = serialOpen(const_cast(static_cast(port)), 115200, 8, 0, 0, nullptr); - if (handle_ <= 0) + if (handle <= 0) { GTEST_SKIP() << "Could not open serial port '" << port << "'. Set SERIAL_TEST_PORT or connect Arduino on /dev/ttyUSB0."; @@ -30,19 +32,19 @@ class SerialArduinoTest : public ::testing::Test void TearDown() override { - if (handle_ > 0) + if (handle > 0) { - serialClose(handle_, nullptr); - handle_ = 0; + serialClose(handle, nullptr); + handle = 0; } } - intptr_t handle_ = 0; + intptr_t handle = 0; }; TEST_F(SerialArduinoTest, OpenClose) { - EXPECT_GT(handle_, 0) << "serialOpen should return a positive handle"; + EXPECT_GT(handle, 0) << "serialOpen should return a positive handle"; } TEST_F(SerialArduinoTest, WriteReadEcho) @@ -50,52 +52,52 @@ TEST_F(SerialArduinoTest, WriteReadEcho) const char *test_message = "Hello Arduino!\n"; int message_len = static_cast(strlen(test_message)); - int written = serialWrite(handle_, test_message, message_len, 1000, 1, nullptr); + int written = serialWrite(handle, test_message, message_len, 1000, 1, nullptr); EXPECT_EQ(written, message_len) << "Should write all bytes. Written: " << written << ", Expected: " << message_len; usleep(500000); - char read_buffer[256] = {0}; - int read_bytes = serialRead(handle_, read_buffer, sizeof(read_buffer) - 1, 2000, 1, nullptr); + std::array read_buffer{}; + int read_bytes = serialRead(handle, read_buffer.data(), static_cast(read_buffer.size()) - 1, 2000, 1, nullptr); EXPECT_GT(read_bytes, 0) << "Should read at least some bytes"; - EXPECT_LE(read_bytes, static_cast(sizeof(read_buffer) - 1)) << "Should not overflow buffer"; + EXPECT_LE(read_bytes, static_cast(read_buffer.size()) - 1) << "Should not overflow buffer"; - read_buffer[read_bytes] = '\0'; - EXPECT_STRNE(read_buffer, "") << "Should receive echo from Arduino"; + read_buffer[static_cast(read_bytes)] = '\0'; + EXPECT_STRNE(read_buffer.data(), "") << "Should receive echo from Arduino"; } TEST_F(SerialArduinoTest, MultipleEchoCycles) { - const char *messages[] = {"Test1\n", "Test2\n", "Test3\n"}; - const int num_messages = 3; + const std::array messages = {"Test1\n", "Test2\n", "Test3\n"}; - for (int i = 0; i < num_messages; ++i) + for (size_t i = 0; i < messages.size(); ++i) { int msg_len = static_cast(strlen(messages[i])); - int written = serialWrite(handle_, messages[i], msg_len, 1000, 1, nullptr); + int written = serialWrite(handle, messages[i], msg_len, 1000, 1, nullptr); EXPECT_EQ(written, msg_len) << "Cycle " << i << ": write failed"; usleep(500000); - char read_buffer[256] = {0}; - int read_bytes = serialRead(handle_, read_buffer, sizeof(read_buffer) - 1, 2000, 1, nullptr); + std::array read_buffer{}; + int read_bytes = + serialRead(handle, read_buffer.data(), static_cast(read_buffer.size()) - 1, 2000, 1, nullptr); EXPECT_GT(read_bytes, 0) << "Cycle " << i << ": read failed"; } } TEST_F(SerialArduinoTest, ReadTimeout) { - char buffer[256]; - int read_bytes = serialRead(handle_, buffer, sizeof(buffer), 100, 1, nullptr); + std::array buffer{}; + int read_bytes = serialRead(handle, buffer.data(), static_cast(buffer.size()), 100, 1, nullptr); EXPECT_GE(read_bytes, 0) << "Timeout should return 0, not error"; } TEST(SerialInvalidHandleTest, InvalidHandleRead) { - char buffer[256]; - int result = serialRead(-1, buffer, sizeof(buffer), 1000, 1, nullptr); + std::array buffer{}; + int result = serialRead(-1, buffer.data(), static_cast(buffer.size()), 1000, 1, nullptr); EXPECT_EQ(result, static_cast(cpp_core::StatusCodes::kInvalidHandleError)) << "Should return error for invalid handle"; }