Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 27 additions & 10 deletions include/libserial/serial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@ size_t getMaxSafeReadSize() const;
*/
int getBaudRate() const;


Comment thread
NestorDP marked this conversation as resolved.
Outdated
DataLength getDataLength() const;

#ifdef BUILD_TESTING_ON
// WARNING: Test helper only! This function bypasses normal initialization
// and may leave the Serial object in an inconsistent state. It is intended
Expand All @@ -341,19 +344,40 @@ void setFdForTest(int fd) {
// WARNING: Test helper only! This function allows injection of custom
// system call functions for testing error handling. It should NEVER be
// used in production code.
void setSystemCallFunctions(
std::function<int(struct pollfd*, nfds_t, int)> poll_func,
std::function<ssize_t(int, void*, size_t)> read_func) {
void setPollSystemFunction(
std::function<int(struct pollfd*, nfds_t, int)> poll_func) {
poll_ = [poll_func](struct pollfd* f, nfds_t n, int t) {
return poll_func(f, n, t);
};
}

void setReadSystemFunction(
std::function<ssize_t(int, void*, size_t)> read_func) {
read_ = [read_func](int fd, void* buf, size_t sz) {
return read_func(fd, buf, sz);
};
}

void setIoctlSystemFunction(
std::function<int(int, unsigned long, void*)> ioctl_func) { // NOLINT
ioctl_ = [ioctl_func](int fd, unsigned long request, void* arg) { // NOLINT
return ioctl_func(fd, request, arg);
};
}
#endif

private:
/**
* @brief Ioctl system call function wrapper
*
* Allows injection of custom ioctl function for testing.
*/
std::function<int(int, unsigned long, void*)> ioctl_ = // NOLINT
[](int fd, unsigned long request,
void* arg) { // NOLINT
return ::ioctl(fd, request, arg);
};
Comment thread
NestorDP marked this conversation as resolved.
Outdated

/**
* @brief Poll system call function wrapper
*
Expand Down Expand Up @@ -456,13 +480,6 @@ uint16_t min_number_char_read_{0};
*/
CanonicalMode canonical_mode_{CanonicalMode::ENABLE};

/**
* @brief Data length setting
*
* Specifies the number of data bits per character (default EIGHT).
*/
DataLength data_length_{DataLength::EIGHT};

/**
* @brief Line terminator character
*
Expand Down
23 changes: 16 additions & 7 deletions src/serial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ size_t Serial::readUntil(std::shared_ptr<std::string> buffer, char terminator) {
}

void Serial::flushInputBuffer() {
if (ioctl(fd_serial_port_, TCFLSH, TCIFLUSH) != 0) {
if (ioctl_(fd_serial_port_, TCFLSH, TCIFLUSH) != 0) {
throw SerialException("Error flushing input buffer: " + std::string(strerror(errno)));
}
}
Expand All @@ -208,7 +208,7 @@ void Serial::setBaudRate(BaudRate baud_rate) {
}

void Serial::setTermios2() {
ssize_t error = ioctl(fd_serial_port_, TCSETS2, &options_);
ssize_t error = ioctl_(fd_serial_port_, TCSETS2, &options_);
if (error < 0) {
throw SerialException("Error set Termios2: " + std::string(strerror(errno)));
}
Expand All @@ -223,10 +223,7 @@ void Serial::setWriteTimeout(std::chrono::milliseconds timeout) {
}

void Serial::setDataLength(DataLength nbits) {
data_length_ = nbits;

this->getTermios2();
// Clear bits
options_.c_cflag &= ~CSIZE;
switch (nbits) {
case DataLength::FIVE:
Expand Down Expand Up @@ -355,7 +352,7 @@ size_t Serial::getMaxSafeReadSize() const {

int Serial::getAvailableData() const {
int bytes_available;
if (ioctl(fd_serial_port_, FIONREAD, &bytes_available) < 0) {
if (ioctl_(fd_serial_port_, FIONREAD, &bytes_available) < 0) {
throw SerialException("Error getting available data: " + std::string(strerror(errno)));
}
return bytes_available;
Expand All @@ -366,8 +363,20 @@ int Serial::getBaudRate() const {
return (static_cast<int>(options_.c_ispeed));
}

DataLength Serial::getDataLength() const {
this->getTermios2();
std::cout << "Getting data length: " << static_cast<int>(options_.c_cflag & CSIZE) << std::endl;
Comment thread
NestorDP marked this conversation as resolved.
Outdated
switch (options_.c_cflag & CSIZE) {
case CS5: return DataLength::FIVE;
case CS6: return DataLength::SIX;
case CS7: return DataLength::SEVEN;
case CS8: return DataLength::EIGHT;
default: return DataLength::EIGHT;
}
}

void Serial::getTermios2() const {
ssize_t error = ioctl(fd_serial_port_, TCGETS2, &options_);
ssize_t error = ioctl_(fd_serial_port_, TCGETS2, &options_);
if (error < 0) {
throw SerialException("Error get Termios2: " + std::string(strerror(errno)));
}
Expand Down
89 changes: 73 additions & 16 deletions test/test_serial_pty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,31 @@ TEST_F(PseudoTerminalTest, ParameterizedConstructor) {
libserial::Serial serial_port(slave_port_);
}

TEST_F(PseudoTerminalTest, SetTermios2WithFail) {
libserial::Serial serial_port;

serial_port.open(slave_port_);

// Inject failure into ioctl for setTermios2
serial_port.setIoctlSystemFunction(
[](int, unsigned long, void*) -> int { // NOLINT
Comment thread
NestorDP marked this conversation as resolved.
errno = EIO;
return -1;
});

EXPECT_THROW({
serial_port.setBaudRate(9600);
}, libserial::SerialException);

// Restore ioctl function for cleanup
serial_port.setIoctlSystemFunction(
[](int fd, unsigned long request, void* arg) -> int { // NOLINT
Comment thread
NestorDP marked this conversation as resolved.
return ::ioctl(fd, request, arg);
});

serial_port.close();
}

TEST_F(PseudoTerminalTest, SetAndGetBaudRate) {
libserial::Serial serial_port;

Expand All @@ -125,6 +150,45 @@ TEST_F(PseudoTerminalTest, SetAndGetBaudRate) {
serial_port.close();
}

// TEST_F(PseudoTerminalTest, SetAndGetDataLength) {
// libserial::Serial serial_port;

// serial_port.open(slave_port_);

// // Test multiple data lengths to be more thorough
// std::vector<libserial::DataLength> test_lengths = {
// libserial::DataLength::FIVE,
// libserial::DataLength::SIX,
// libserial::DataLength::SEVEN,
// libserial::DataLength::EIGHT
// };

// for (const auto& expected_length : test_lengths) {
// // Set data length
// EXPECT_NO_THROW({
// serial_port.setDataLength(expected_length);
// });

// // Add a small delay and flush
// std::this_thread::sleep_for(std::chrono::milliseconds(50));

// // Force a re-read of the current settings
// serial_port.close();
// serial_port.open(slave_port_);

// // Get data length and verify
// libserial::DataLength actual_length;
// EXPECT_NO_THROW({
// actual_length = serial_port.getDataLength();
// });

// EXPECT_EQ(actual_length, expected_length)
// << "Failed for data length: " << static_cast<int>(expected_length);
// }

// serial_port.close();
// }

TEST_F(PseudoTerminalTest, SetParity) {
libserial::Serial serial_port;

Expand Down Expand Up @@ -309,10 +373,11 @@ TEST_F(PseudoTerminalTest, ReadWithReadFail) {
auto read_buffer = std::make_shared<std::string>();

for (const auto& [error_num, error_msg] : errors_read_) {
serial_port.setSystemCallFunctions(
serial_port.setPollSystemFunction(
[](struct pollfd*, nfds_t, int) -> int {
return 1;
},
});
serial_port.setReadSystemFunction(
[error_num](int, void*, size_t) -> ssize_t {
errno = error_num;
return -1;
Expand All @@ -337,13 +402,10 @@ TEST_F(PseudoTerminalTest, ReadWithPollFail) {
auto read_buffer = std::make_shared<std::string>();

for (const auto& [error_num, error_msg] : errors_poll_) {
serial_port.setSystemCallFunctions(
serial_port.setPollSystemFunction(
[error_num](struct pollfd*, nfds_t, int) -> int {
errno = error_num;
return -1;
},
[](int, void*, size_t) -> ssize_t {
return 1;
});

auto expected_what = "Error in poll(): " + error_msg;
Expand Down Expand Up @@ -436,10 +498,7 @@ TEST_F(PseudoTerminalTest, ReadBytesWithReadFail) {
auto read_buffer = std::make_shared<std::string>();

for (const auto& [error_num, error_msg] : errors_read_) {
serial_port.setSystemCallFunctions(
[](struct pollfd*, nfds_t, int) -> int {
return 1;
},
serial_port.setReadSystemFunction(
[error_num](int, void*, size_t) -> ssize_t {
errno = error_num;
return -1;
Expand Down Expand Up @@ -554,10 +613,11 @@ TEST_F(PseudoTerminalTest, ReadUntilWithReadFail) {
// Skip these as they are handled differently in readUntil
continue;
}
serial_port.setSystemCallFunctions(
serial_port.setPollSystemFunction(
[](struct pollfd*, nfds_t, int) -> int {
return 1;
},
});
serial_port.setReadSystemFunction(
[error_num](int, void*, size_t) -> ssize_t {
errno = error_num;
return -1;
Expand All @@ -582,13 +642,10 @@ TEST_F(PseudoTerminalTest, ReadUntilWithPollFail) {
auto read_buffer = std::make_shared<std::string>();

for (const auto& [error_num, error_msg] : errors_poll_) {
serial_port.setSystemCallFunctions(
serial_port.setPollSystemFunction(
[error_num](struct pollfd*, nfds_t, int) -> int {
errno = error_num;
return -1;
},
[](int, void*, size_t) -> ssize_t {
return 1;
});

auto expected_what = "Error in poll(): " + error_msg;
Expand Down
Loading