Files
IoTManager/lib/ArduinoStreamUtils/extras/test/ReadBufferingStreamTest.cpp
Dmitry Borisenko 24a9d55ea0 first
2021-12-22 14:09:50 +01:00

319 lines
7.4 KiB
C++

// StreamUtils - github.com/bblanchon/ArduinoStreamUtils
// Copyright Benoit Blanchon 2019-2021
// MIT License
#include "FailingAllocator.hpp"
#include "StreamUtils/Prints/StringPrint.hpp"
#include "StreamUtils/Streams/ReadBufferingStream.hpp"
#include "StreamUtils/Streams/SpyingStream.hpp"
#include "StreamUtils/Streams/StringStream.hpp"
#include "doctest.h"
using namespace StreamUtils;
TEST_CASE("ReadBufferingStream") {
StringStream upstream;
StringPrint log;
SpyingStream spy{upstream, log};
SUBCASE("capacity = 4") {
ReadBufferingStream bufferedStream{spy, 4};
Stream& stream = bufferedStream;
SUBCASE("available()") {
SUBCASE("empty input") {
CHECK(stream.available() == 0);
CHECK(log.str() == "available() -> 0");
}
SUBCASE("read empty input") {
int n = stream.read();
CHECK(n == -1);
CHECK(stream.available() == 0);
CHECK(log.str() ==
"available() -> 0"
"read() -> -1"
"available() -> 0");
}
SUBCASE("same a upstream") {
upstream.print("ABCDEFGH");
CHECK(stream.available() == 8);
CHECK(log.str() == "available() -> 8");
}
SUBCASE("upstream + in buffer") {
upstream.print("ABCDEFGH");
int n = stream.read();
CHECK(n == 'A');
CHECK(stream.available() == 7);
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 8"
"readBytes(4) -> 4"
"available() -> 4");
#endif
}
}
SUBCASE("peek()") {
SUBCASE("returns -1 when empty") {
upstream.flush();
int result = stream.peek();
CHECK(result == -1);
CHECK(log.str() == "peek() -> -1");
}
SUBCASE("doesn't call readBytes() when buffer is empty") {
upstream.print("A");
int result = stream.peek();
CHECK(result == 'A');
CHECK(log.str() == "peek() -> 65");
}
SUBCASE("doesn't call peek() when buffer is full") {
upstream.print("AB");
stream.read();
int result = stream.peek();
CHECK(result == 'B');
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 2"
"readBytes(2) -> 2");
#endif
}
}
SUBCASE("read()") {
SUBCASE("reads 4 bytes at a time") {
upstream.print("ABCDEFG");
std::string result;
for (int i = 0; i < 7; i++) {
result += (char)stream.read();
}
CHECK(result == "ABCDEFG");
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 7"
"readBytes(4) -> 4"
"available() -> 3"
"readBytes(3) -> 3");
#endif
}
SUBCASE("returns -1 when empty") {
upstream.flush();
int result = stream.read();
CHECK(result == -1);
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 0"
"read() -> -1");
#endif
}
}
SUBCASE("readBytes()") {
SUBCASE("empty input") {
upstream.flush();
char c;
size_t result = stream.readBytes(&c, 1);
CHECK(result == 0);
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 0"
"readBytes(1) -> 0 [timeout]");
#endif
}
SUBCASE("reads 4 bytes when requested one") {
upstream.print("ABCDEFG");
char c;
size_t result = stream.readBytes(&c, 1);
CHECK(c == 'A');
CHECK(result == 1);
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 7"
"readBytes(4) -> 4");
#endif
}
SUBCASE("copy one byte from buffer") {
upstream.print("ABCDEFGH");
stream.read(); // load buffer
char c;
size_t result = stream.readBytes(&c, 1);
CHECK(c == 'B');
CHECK(result == 1);
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 8"
"readBytes(4) -> 4");
#endif
}
SUBCASE("copy content from buffer then bypass buffer") {
upstream.print("ABCDEFGH");
stream.read(); // load buffer
char c[8] = {0};
size_t result = stream.readBytes(c, 7);
CHECK(c == std::string("BCDEFGH"));
CHECK(result == 7);
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 8"
"readBytes(4) -> 4"
"available() -> 4"
"readBytes(4) -> 4");
#endif
}
SUBCASE("copy content from buffer twice") {
upstream.print("ABCDEFGH");
stream.read(); // load buffer
char c[8] = {0};
size_t result = stream.readBytes(c, 4);
CHECK(c == std::string("BCDE"));
CHECK(result == 4);
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 8"
"readBytes(4) -> 4"
"available() -> 4"
"readBytes(4) -> 4");
#endif
}
SUBCASE("read past the end") {
upstream.print("A");
char c;
stream.readBytes(&c, 1);
size_t result = stream.readBytes(&c, 1);
CHECK(result == 0);
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 1"
"readBytes(1) -> 1"
"available() -> 0"
"readBytes(1) -> 0 [timeout]");
#endif
}
}
SUBCASE("flush()") {
stream.flush();
CHECK(log.str() == "flush()");
}
SUBCASE("copy constructor") {
upstream.print("ABCDEFGH");
bufferedStream.read();
auto dup = bufferedStream;
int result = dup.read();
CHECK(result == 'B');
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 8"
"readBytes(4) -> 4");
#endif
}
}
SUBCASE("No memory") {
BasicReadBufferingStream<FailingAllocator> stream(spy, 4);
SUBCASE("available()") {
upstream.print("ABC");
CHECK(stream.available() == 3);
}
// SUBCASE("capacity()") {
// CHECK(stream.capacity() == 0);
// }
SUBCASE("peek()") {
upstream.print("ABC");
int c = stream.peek();
CHECK(c == 'A');
CHECK(log.str() == "peek() -> 65");
}
SUBCASE("read()") {
upstream.print("ABC");
int c = stream.read();
CHECK(c == 'A');
CHECK(log.str() == "read() -> 65");
}
SUBCASE("readBytes()") {
upstream.print("ABC");
char s[4] = {0};
int n = stream.readBytes(s, 3);
CHECK(n == 3);
CHECK(s == std::string("ABC"));
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() == "readBytes(3) -> 3");
#endif
}
}
SUBCASE("Real example") {
ReadBufferingStream bufferedStream{spy, 64};
Stream& stream = bufferedStream;
upstream.print("{\"helloWorld\":\"Hello World\"}");
char c[] = "ABCDEFGH";
CHECK(stream.readBytes(&c[0], 1) == 1);
CHECK(stream.readBytes(&c[1], 1) == 1);
CHECK(stream.readBytes(&c[2], 1) == 1);
CHECK(stream.readBytes(&c[3], 1) == 1);
CHECK(c == std::string("{\"heEFGH"));
#if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL
CHECK(log.str() ==
"available() -> 28"
"readBytes(28) -> 28");
#endif
}
}