Skip to content

Commit a63505b

Browse files
authored
🔀 Merge pull request #16 from NestorDP/feat-improve-read-tests
Feat improve read tests
2 parents 01594b6 + 7ad8e49 commit a63505b

3 files changed

Lines changed: 126 additions & 8 deletions

File tree

‎include/libserial/serial.hpp‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,26 @@ void setMinNumberCharRead(uint16_t);
302302
*/
303303
void setBaudRate(BaudRate baud_rate);
304304

305+
/**
306+
* @brief Sets the maximum safe read size
307+
*
308+
* Configures the maximum number of bytes that can be read
309+
* in a single read operation to prevent excessive memory usage.
310+
*
311+
* @param size The desired maximum safe read size in bytes
312+
*/
313+
void setMaxSafeReadSize(size_t size);
314+
315+
/**
316+
* @brief Gets the maximum safe read size
317+
*
318+
* Retrieves the maximum number of bytes that can be read
319+
* in a single read operation to prevent excessive memory usage.
320+
*
321+
* @return The maximum safe read size in bytes
322+
*/
323+
size_t getMaxSafeReadSize() const;
324+
305325
/**
306326
* @brief Gets the current baud rate
307327
*
@@ -318,8 +338,9 @@ int getBaudRate() const;
318338
void setFdForTest(int fd) {
319339
fd_serial_port_ = fd;
320340
}
321-
322-
// For testing - allow injection of mock functions
341+
// WARNING: Test helper only! This function allows injection of custom
342+
// system call functions for testing error handling. It should NEVER be
343+
// used in production code.
323344
void setSystemCallFunctions(
324345
std::function<int(struct pollfd*, nfds_t, int)> poll_func,
325346
std::function<ssize_t(int, void*, size_t)> read_func) {
@@ -410,8 +431,9 @@ std::chrono::milliseconds write_timeout_ms_{1000}; ///< Write timeout in mill
410431
*
411432
* Defines the maximum number of bytes that can be read
412433
* in a single read operation to prevent excessive memory usage.
434+
* Default is 2048 bytes (2KB).
413435
*/
414-
static constexpr size_t kMaxSafeReadSize = 2048; // 2KB limit
436+
size_t max_safe_read_size_{2048}; // 2KB limit
415437

416438
/**
417439
* @brief Timeout value in milliseconds

‎src/serial.cpp‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ size_t Serial::read(std::shared_ptr<std::string> buffer) {
6565
}
6666

6767
buffer->clear();
68-
buffer->resize(kMaxSafeReadSize);
68+
buffer->resize(max_safe_read_size_);
6969

7070
struct pollfd fd_poll;
7171
fd_poll.fd = fd_serial_port_;
@@ -83,7 +83,8 @@ size_t Serial::read(std::shared_ptr<std::string> buffer) {
8383
}
8484

8585
// Data available: do the read
86-
ssize_t bytes_read = read_(fd_serial_port_, const_cast<char*>(buffer->data()), kMaxSafeReadSize);
86+
ssize_t bytes_read = read_(fd_serial_port_, const_cast<char*>(buffer->data()),
87+
max_safe_read_size_);
8788
if (bytes_read < 0) {
8889
throw IOException(std::string("Error reading from serial port: ") + strerror(errno));
8990
}
@@ -108,7 +109,7 @@ size_t Serial::readBytes(std::shared_ptr<std::string> buffer, size_t num_bytes)
108109
buffer->clear();
109110
buffer->resize(num_bytes);
110111

111-
ssize_t bytes_read = ::read(fd_serial_port_, buffer->data(), num_bytes); // codacy-ignore[buffer-boundary]
112+
ssize_t bytes_read = read_(fd_serial_port_, buffer->data(), num_bytes); // codacy-ignore[buffer-boundary]
112113

113114
if (bytes_read < 0) {
114115
throw IOException("Error reading from serial port: " + std::string(strerror(errno)));
@@ -130,9 +131,9 @@ size_t Serial::readUntil(std::shared_ptr<std::string> buffer, char terminator) {
130131

131132
while (temp_char != terminator) {
132133
// Check buffer size limit to prevent excessive memory usage
133-
if (buffer->size() >= kMaxSafeReadSize) {
134+
if (buffer->size() >= max_safe_read_size_) {
134135
throw IOException("Read buffer exceeded maximum size limit of " +
135-
std::to_string(kMaxSafeReadSize) +
136+
std::to_string(max_safe_read_size_) +
136137
" bytes without finding terminator");
137138
}
138139
// Check timeout if enabled (0 means no timeout)
@@ -344,6 +345,14 @@ void Serial::setMinNumberCharRead(uint16_t num) {
344345
this->setTermios2();
345346
}
346347

348+
void Serial::setMaxSafeReadSize(size_t size) {
349+
max_safe_read_size_ = size;
350+
}
351+
352+
size_t Serial::getMaxSafeReadSize() const {
353+
return max_safe_read_size_;
354+
}
355+
347356
int Serial::getAvailableData() const {
348357
int bytes_available;
349358
if (ioctl(fd_serial_port_, FIONREAD, &bytes_available) < 0) {

‎test/test_serial_pty.cpp‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,39 @@ TEST_F(PseudoTerminalTest, ReadBytesWithInvalidNumBytes) {
426426
}, libserial::IOException);
427427
}
428428

429+
TEST_F(PseudoTerminalTest, ReadBytesWithReadFail) {
430+
libserial::Serial serial_port;
431+
432+
serial_port.open(slave_port_);
433+
serial_port.setBaudRate(9600);
434+
serial_port.setCanonicalMode(libserial::CanonicalMode::DISABLE);
435+
436+
auto read_buffer = std::make_shared<std::string>();
437+
438+
for (const auto& [error_num, error_msg] : errors_read_) {
439+
serial_port.setSystemCallFunctions(
440+
[](struct pollfd*, nfds_t, int) -> int {
441+
return 1;
442+
},
443+
[error_num](int, void*, size_t) -> ssize_t {
444+
errno = error_num;
445+
return -1;
446+
});
447+
448+
auto expected_what = "Error reading from serial port: " + error_msg;
449+
450+
EXPECT_THROW({
451+
try {
452+
serial_port.readBytes(read_buffer, 10);
453+
}
454+
catch (const libserial::IOException& e) {
455+
EXPECT_STREQ(expected_what.c_str(), e.what());
456+
throw;
457+
}
458+
}, libserial::IOException);
459+
}
460+
}
461+
429462
TEST_F(PseudoTerminalTest, ReadBytesCanonicalMode) {
430463
libserial::Serial serial_port;
431464

@@ -472,6 +505,25 @@ TEST_F(PseudoTerminalTest, ReadUntil) {
472505
EXPECT_EQ(*read_buffer, "Read Until!");
473506
}
474507

508+
TEST_F(PseudoTerminalTest, ReadUntilWithNullBuffer) {
509+
libserial::Serial serial_port;
510+
511+
serial_port.open(slave_port_);
512+
serial_port.setBaudRate(9600);
513+
514+
std::shared_ptr<std::string> null_buffer;
515+
516+
EXPECT_THROW({
517+
try {
518+
serial_port.readUntil(null_buffer, '!');
519+
}
520+
catch (const libserial::IOException& e) {
521+
EXPECT_STREQ("Null pointer passed to readUntil function", e.what());
522+
throw;
523+
}
524+
}, libserial::IOException);
525+
}
526+
475527
TEST_F(PseudoTerminalTest, ReadUntilTimeout) {
476528
libserial::Serial serial_port;
477529

@@ -552,3 +604,38 @@ TEST_F(PseudoTerminalTest, ReadUntilWithPollFail) {
552604
}, libserial::IOException);
553605
}
554606
}
607+
608+
TEST_F(PseudoTerminalTest, ReadUntilWithOverflowBuffer) {
609+
libserial::Serial serial_port;
610+
611+
serial_port.open(slave_port_);
612+
serial_port.setBaudRate(9600);
613+
EXPECT_NO_THROW(serial_port.setMaxSafeReadSize(10)); // Set max safe read size to 10 bytes
614+
615+
std::string test_message(15, 'a');
616+
test_message.push_back('\n');
617+
618+
ssize_t bytes_written = write(master_fd_, test_message.c_str(), test_message.length());
619+
ASSERT_GT(bytes_written, 0) << "Failed to write to master end";
620+
621+
// Give time for data to propagate
622+
fsync(master_fd_);
623+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
624+
625+
// Test reading with shared pointer - only read what's available
626+
auto read_buffer = std::make_shared<std::string>();
627+
628+
auto expected_what = "Read buffer exceeded maximum size limit of " +
629+
std::to_string(serial_port.getMaxSafeReadSize()) +
630+
" bytes without finding terminator";
631+
632+
EXPECT_THROW({
633+
try {
634+
serial_port.readUntil(read_buffer, '!');
635+
}
636+
catch (const libserial::IOException& e) {
637+
EXPECT_STREQ(expected_what.c_str(), e.what());
638+
throw;
639+
}
640+
}, libserial::IOException);
641+
}

0 commit comments

Comments
 (0)