mirror of
https://github.com/esphome/esphome.git
synced 2026-10-18 04:02:54 +00:00
181 lines
5.0 KiB
C++
181 lines
5.0 KiB
C++
#include <gtest/gtest.h>
|
|
#include <string>
|
|
#include <vector>
|
|
#include "esphome/core/line_reader.h"
|
|
|
|
namespace esphome::core::testing {
|
|
|
|
using Lines = std::vector<std::string>;
|
|
|
|
// Feeds a string and collects every completed line.
|
|
template<typename R> static Lines feed_all(R &reader, const std::string &input) {
|
|
Lines lines;
|
|
for (char c : input) {
|
|
const char *line = reader.feed(static_cast<uint8_t>(c));
|
|
if (line != nullptr) {
|
|
EXPECT_EQ(reader.length(), 0u);
|
|
lines.emplace_back(line);
|
|
}
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
static_assert(sizeof(LineReader<80>) == 81);
|
|
static_assert(sizeof(LineReader<1025>) == 1028);
|
|
// 0xFD is the largest size with a one byte index; 0xFE switches to two bytes
|
|
static_assert(sizeof(LineReader<0xFD>) == 0xFE);
|
|
static_assert(sizeof(LineReader<0xFE>) == 0x100);
|
|
|
|
TEST(LineReaderTest, CrLfLines) {
|
|
LineReader<16> reader;
|
|
EXPECT_EQ(feed_all(reader, "abc\r\ndef\r\n"), (Lines{"abc", "def"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, LfOnlyLines) {
|
|
LineReader<16> reader;
|
|
EXPECT_EQ(feed_all(reader, "abc\ndef\n"), (Lines{"abc", "def"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, CrOnlyLinesWithCrTerminator) {
|
|
LineReader<16, '\r', '\n'> reader;
|
|
EXPECT_EQ(feed_all(reader, "R1234\rR0567\r"), (Lines{"R1234", "R0567"}));
|
|
// The LF of a CRLF pair is ignored with this setup
|
|
EXPECT_EQ(feed_all(reader, "x\r\ny\r\n"), (Lines{"x", "y"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, CrInsideLineIsIgnored) {
|
|
LineReader<16> reader;
|
|
EXPECT_EQ(feed_all(reader, "a\rb\n"), (Lines{"ab"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, EmptyLinesAreReturned) {
|
|
LineReader<16> reader;
|
|
EXPECT_EQ(feed_all(reader, "\r\n\nx\n"), (Lines{"", "", "x"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, LineOfExactlyMaxLengthFits) {
|
|
LineReader<6> reader;
|
|
EXPECT_EQ(feed_all(reader, "12345\n"), (Lines{"12345"}));
|
|
EXPECT_FALSE(reader.overflowed());
|
|
}
|
|
|
|
TEST(LineReaderTest, OverlongLineIsDroppedUpToTerminator) {
|
|
LineReader<6> reader;
|
|
int overflow_count = 0;
|
|
Lines lines;
|
|
for (char c : std::string("123456789\r\nok\n")) {
|
|
const char *line = reader.feed(c);
|
|
if (reader.overflowed()) {
|
|
overflow_count++;
|
|
EXPECT_STREQ(reader.partial(), "12345");
|
|
EXPECT_EQ(reader.length(), 0u);
|
|
reader.log_if_overflowed("line_reader_test");
|
|
}
|
|
if (line != nullptr)
|
|
lines.emplace_back(line);
|
|
}
|
|
EXPECT_EQ(overflow_count, 1);
|
|
EXPECT_EQ(lines, (Lines{"ok"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, SixteenBitIndexOverflowAndRecovery) {
|
|
LineReader<300> reader;
|
|
const std::string full(299, 'a');
|
|
EXPECT_EQ(feed_all(reader, full + "\n"), (Lines{full}));
|
|
int overflow_count = 0;
|
|
Lines lines;
|
|
for (char c : full + "bcd\r\nok\n") {
|
|
const char *line = reader.feed(c);
|
|
if (reader.overflowed()) {
|
|
overflow_count++;
|
|
EXPECT_EQ(std::string(reader.partial()), full);
|
|
}
|
|
if (line != nullptr)
|
|
lines.emplace_back(line);
|
|
}
|
|
EXPECT_EQ(overflow_count, 1);
|
|
EXPECT_EQ(lines, (Lines{"ok"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, ResetAfterOverflowRestartsAtNextByte) {
|
|
LineReader<4> reader;
|
|
Lines lines;
|
|
for (char c : std::string("abcdefg\n")) {
|
|
const char *line = reader.feed(c);
|
|
if (reader.overflowed())
|
|
reader.reset();
|
|
if (line != nullptr)
|
|
lines.emplace_back(line);
|
|
}
|
|
// "abc" fills the buffer, 'd' overflows and is dropped, "efg" is the next line
|
|
EXPECT_EQ(lines, (Lines{"efg"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, RestartWithKeepsOverflowByte) {
|
|
LineReader<4> reader;
|
|
Lines lines;
|
|
for (char c : std::string("abcdef\n")) {
|
|
const char *line = reader.feed(c);
|
|
if (reader.overflowed()) {
|
|
reader.restart_with(c);
|
|
}
|
|
if (line != nullptr) {
|
|
lines.emplace_back(line);
|
|
}
|
|
}
|
|
// 'd' overflows and starts the next line
|
|
EXPECT_EQ(lines, (Lines{"def"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, FeedReportsLength) {
|
|
LineReader<16> reader;
|
|
size_t len = 99;
|
|
char *line = nullptr;
|
|
for (char c : std::string("ab\rc\n")) {
|
|
line = reader.feed(c, len);
|
|
}
|
|
ASSERT_NE(line, nullptr);
|
|
EXPECT_STREQ(line, "abc");
|
|
EXPECT_EQ(len, 3u);
|
|
EXPECT_NE(reader.feed('\n', len), nullptr);
|
|
EXPECT_EQ(len, 0u);
|
|
}
|
|
|
|
TEST(LineReaderTest, PartialForPromptWithoutLineEnd) {
|
|
LineReader<32> reader;
|
|
EXPECT_EQ(feed_all(reader, "DFRobot:/>"), Lines{});
|
|
EXPECT_STREQ(reader.partial(), "DFRobot:/>");
|
|
EXPECT_EQ(reader.length(), 10u);
|
|
reader.reset();
|
|
EXPECT_STREQ(reader.partial(), "");
|
|
EXPECT_EQ(feed_all(reader, "sensorStart\r\n"), (Lines{"sensorStart"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, DiscardLineDropsUntilTerminator) {
|
|
LineReader<16> reader;
|
|
feed_all(reader, "half");
|
|
reader.discard_line();
|
|
EXPECT_EQ(reader.length(), 0u);
|
|
EXPECT_STREQ(reader.partial(), "");
|
|
EXPECT_EQ(feed_all(reader, "rest\nnext\n"), (Lines{"next"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, CustomIgnoreByte) {
|
|
LineReader<8, '\r', static_cast<char>(0xFF)> reader;
|
|
EXPECT_EQ(feed_all(reader, "R12\xFF"
|
|
"34\r"),
|
|
(Lines{"R1234"}));
|
|
}
|
|
|
|
TEST(LineReaderTest, LineIsWritable) {
|
|
LineReader<16> reader;
|
|
char *line = nullptr;
|
|
for (char c : std::string("a,b\n"))
|
|
line = reader.feed(c);
|
|
ASSERT_NE(line, nullptr);
|
|
line[1] = '\0';
|
|
EXPECT_STREQ(line, "a");
|
|
}
|
|
|
|
} // namespace esphome::core::testing
|