Skip to content

Commit ab70d94

Browse files
committed
Refactor serial read functions to enhance timeout handling and improve test coverage
- Renamed `readUntilNotReady` to `readUntilTimeoutOrFull` for clarity and updated its parameters to include a per-iteration timeout. - Modified the `serialRead` function to utilize the new read function, allowing for more flexible timeout management based on a multiplier. - Added a new utility function `drainInput` in tests to facilitate input handling during tests. - Introduced new test cases to validate timeout behavior when reading from the serial interface, ensuring robustness in scenarios with no data available.
1 parent 314c2e6 commit ab70d94

2 files changed

Lines changed: 62 additions & 5 deletions

File tree

src/serial_read.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@ auto handleWaitResultForRead(int wait_result, int abort_fd, ErrorCallbackT error
4141
return std::nullopt;
4242
}
4343

44-
auto readUntilNotReady(int fd, int abort_fd, unsigned char *buf, int buffer_size, int already_read,
45-
ErrorCallbackT error_callback) -> int
44+
auto readUntilTimeoutOrFull(int fd, int abort_fd, unsigned char *buf, int buffer_size, int already_read,
45+
int per_iteration_timeout_ms, ErrorCallbackT error_callback) -> int
4646
{
4747
int total_read = already_read;
4848
while (total_read < buffer_size)
4949
{
50-
const int wait_result = cpp_bindings_linux::detail::waitFdReadyOrAbort(fd, abort_fd, 0, true);
50+
const int wait_result =
51+
cpp_bindings_linux::detail::waitFdReadyOrAbort(fd, abort_fd, per_iteration_timeout_ms, true);
5152
if (const auto immediate = handleWaitResultForRead(wait_result, abort_fd, error_callback);
5253
immediate.has_value())
5354
{
@@ -66,6 +67,7 @@ auto readUntilNotReady(int fd, int abort_fd, unsigned char *buf, int buffer_size
6667
}
6768
if (more_bytes == 0)
6869
{
70+
// Driver reported readiness but returned nothing; treat like "no more data".
6971
return total_read;
7072
}
7173
total_read += static_cast<int>(more_bytes);
@@ -76,7 +78,7 @@ auto readUntilNotReady(int fd, int abort_fd, unsigned char *buf, int buffer_size
7678

7779
extern "C"
7880
{
79-
MODULE_API auto serialRead(int64_t handle, void *buffer, int buffer_size, int timeout_ms, int /*multiplier*/,
81+
MODULE_API auto serialRead(int64_t handle, void *buffer, int buffer_size, int timeout_ms, int multiplier,
8082
ErrorCallbackT error_callback) -> int
8183
{
8284
if (buffer == nullptr || buffer_size <= 0)
@@ -127,7 +129,21 @@ extern "C"
127129
}
128130
}
129131

130-
return readUntilNotReady(fd, abort_fd, buf, buffer_size, static_cast<int>(bytes_read), error_callback);
132+
const int already_read = static_cast<int>(bytes_read);
133+
134+
if (multiplier == 0)
135+
{
136+
return already_read;
137+
}
138+
139+
const int per_byte_timeout_ms = timeout_ms * multiplier;
140+
if (per_byte_timeout_ms <= 0)
141+
{
142+
return readUntilTimeoutOrFull(fd, abort_fd, buf, buffer_size, already_read, 0, error_callback);
143+
}
144+
145+
return readUntilTimeoutOrFull(fd, abort_fd, buf, buffer_size, already_read, per_byte_timeout_ms,
146+
error_callback);
131147
}
132148

133149
} // extern "C"

tests/test_serial_arduino.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,26 @@
2020
#include <thread>
2121
#include <unistd.h>
2222

23+
namespace
24+
{
25+
auto drainInput(intptr_t handle) -> void
26+
{
27+
std::array<unsigned char, 256> tmp = {};
28+
for (;;)
29+
{
30+
const int res = serialRead(handle, tmp.data(), static_cast<int>(tmp.size()), 10, 0, nullptr);
31+
if (res < 0)
32+
{
33+
FAIL() << "drainInput failed with error " << res;
34+
}
35+
if (res == 0)
36+
{
37+
return;
38+
}
39+
}
40+
}
41+
} // namespace
42+
2343
class SerialArduinoTest : public ::testing::Test
2444
{
2545
protected:
@@ -108,6 +128,27 @@ TEST_F(SerialArduinoTest, ReadTimeout)
108128
EXPECT_GE(read_bytes, 0) << "Timeout should return 0, not error";
109129
}
110130

131+
TEST_F(SerialArduinoTest, Read60BytesWithoutWritingTimesOut)
132+
{
133+
drainInput(handle);
134+
std::array<unsigned char, 60> buffer = {};
135+
const int read_bytes = serialRead(handle, buffer.data(), static_cast<int>(buffer.size()), 200, 1, nullptr);
136+
EXPECT_EQ(read_bytes, 0) << "Expected timeout (0 bytes) when no data is sent";
137+
}
138+
139+
TEST_F(SerialArduinoTest, Write10BytesRead60Returns10ThenTimesOut)
140+
{
141+
drainInput(handle);
142+
const std::array<unsigned char, 10> payload = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
143+
const int written = serialWrite(handle, payload.data(), static_cast<int>(payload.size()), 2000, 1, nullptr);
144+
ASSERT_EQ(written, static_cast<int>(payload.size()));
145+
146+
std::array<unsigned char, 60> buffer = {};
147+
const int read_bytes = serialRead(handle, buffer.data(), static_cast<int>(buffer.size()), 200, 1, nullptr);
148+
EXPECT_EQ(read_bytes, static_cast<int>(payload.size()))
149+
<< "Expected to read the 10 echoed bytes, then timeout waiting for more";
150+
}
151+
111152
TEST_F(SerialArduinoTest, AbortRead)
112153
{
113154
std::atomic<int> read_result{999};

0 commit comments

Comments
 (0)