#include #include "common.h" #include "esphome/components/uart/uart.h" namespace esphome::uart::testing { // Feeds bytes from a fixed vector so available() and read_array() agree like a real UART. class FakeRxUART : public MockUARTComponent { public: explicit FakeRxUART(std::vector rx) : rx_(std::move(rx)) { ON_CALL(*this, available()).WillByDefault(Invoke([this]() { return this->rx_.size() - this->pos_; })); ON_CALL(*this, read_array(_, _)).WillByDefault(Invoke([this](uint8_t *data, size_t len) { this->read_sizes.push_back(len); if (this->fail_after_reads >= 0 && static_cast(this->read_sizes.size()) > this->fail_after_reads) return false; if (len > this->rx_.size() - this->pos_) return false; std::memcpy(data, this->rx_.data() + this->pos_, len); this->pos_ += len; return true; })); } void append(std::initializer_list more) { this->rx_.insert(this->rx_.end(), more); } size_t remaining() const { return this->rx_.size() - this->pos_; } std::vector read_sizes; int fail_after_reads{-1}; protected: std::vector rx_; size_t pos_{0}; }; static std::vector make_bytes(size_t n) { std::vector v(n); for (size_t i = 0; i < n; i++) v[i] = static_cast(i); return v; } TEST(UARTReadAvailableTest, NothingAvailableDoesNotRead) { ::testing::NiceMock uart(std::vector{}); UARTDevice dev(&uart); int calls = 0; EXPECT_TRUE(dev.read_available([&calls](uint8_t) { calls++; })); EXPECT_EQ(calls, 0); EXPECT_TRUE(uart.read_sizes.empty()); } TEST(UARTReadAvailableTest, ReadsEveryByteInChunks) { ::testing::NiceMock uart(make_bytes(10)); UARTDevice dev(&uart); std::vector got; EXPECT_TRUE(dev.read_available<4>([&got](uint8_t b) { got.push_back(b); })); EXPECT_EQ(got, make_bytes(10)); EXPECT_EQ(uart.read_sizes, (std::vector{4, 4, 2})); } TEST(UARTReadAvailableTest, OnlyBytesAvailableOnEntry) { ::testing::NiceMock uart(std::vector{1, 2, 3}); UARTDevice dev(&uart); std::vector got; EXPECT_TRUE(dev.read_available<2>([&](uint8_t b) { got.push_back(b); if (b == 1) uart.append({9, 9}); // arrives while draining; left for the next call })); EXPECT_EQ(got, (std::vector{1, 2, 3})); EXPECT_EQ(uart.remaining(), 2u); } TEST(UARTReadAvailableTest, CountReadsExactlyThatMany) { ::testing::NiceMock uart(make_bytes(100)); UARTDevice dev(&uart); size_t count = 0; EXPECT_TRUE(dev.read_available<16>([&count](uint8_t) { count++; }, 40)); EXPECT_EQ(count, 40u); EXPECT_EQ(uart.read_sizes, (std::vector{16, 16, 8})); EXPECT_EQ(uart.remaining(), 60u); } TEST(UARTReadAvailableTest, ReadFailureStopsAndReturnsFalse) { ::testing::NiceMock uart(make_bytes(10)); uart.fail_after_reads = 1; UARTDevice dev(&uart); std::vector got; EXPECT_FALSE(dev.read_available<4>([&got](uint8_t b) { got.push_back(b); })); EXPECT_EQ(got, (std::vector{0, 1, 2, 3})); EXPECT_EQ(uart.read_sizes.size(), 2u); } TEST(UARTReadAvailableTest, ByteCallbackReturningFalseStops) { ::testing::NiceMock uart(make_bytes(10)); UARTDevice dev(&uart); std::vector got; EXPECT_TRUE(dev.read_available<4>([&got](uint8_t b) { got.push_back(b); return b != 5; })); // Stops mid chunk: bytes 6 and 7 were read from the UART but not handed over, 8 and 9 were never read. EXPECT_EQ(got, (std::vector{0, 1, 2, 3, 4, 5})); EXPECT_EQ(uart.remaining(), 2u); } TEST(UARTReadAvailableTest, ChunkCallbackGetsWholeChunks) { ::testing::NiceMock uart(make_bytes(70)); UARTDevice dev(&uart); std::vector lens; std::vector got; EXPECT_TRUE(dev.read_available_chunks([&](const uint8_t *data, size_t len) { lens.push_back(len); got.insert(got.end(), data, data + len); })); EXPECT_EQ(lens, (std::vector{64, 6})); EXPECT_EQ(got, make_bytes(70)); } TEST(UARTReadAvailableTest, ChunkCallbackReturningFalseStops) { ::testing::NiceMock uart(make_bytes(20)); UARTDevice dev(&uart); int chunks = 0; EXPECT_TRUE(dev.read_available_chunks<8>([&chunks](const uint8_t *, size_t) { chunks++; return false; })); EXPECT_EQ(chunks, 1); EXPECT_EQ(uart.remaining(), 12u); } TEST(UARTReadAvailableTest, ChunksWithCountStopEarly) { ::testing::NiceMock uart(make_bytes(30)); UARTDevice dev(&uart); std::vector lens; EXPECT_TRUE(dev.read_available_chunks<8>( [&lens](const uint8_t *, size_t len) { lens.push_back(len); return lens.size() < 2; }, 20)); EXPECT_EQ(lens, (std::vector{8, 8})); EXPECT_EQ(uart.remaining(), 14u); } } // namespace esphome::uart::testing