//MIT License // //Copyright (c) 2019 Mindaugas Vinkelis // //Permission is hereby granted, free of charge, to any person obtaining a copy //of this software and associated documentation files (the "Software"), to deal //in the Software without restriction, including without limitation the rights //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell //copies of the Software, and to permit persons to whom the Software is //furnished to do so, subject to the following conditions: // //The above copyright notice and this permission notice shall be included in all //copies or substantial portions of the Software. // //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //SOFTWARE. #include #include #include #include #include #include #include #include //some helper types using Buffer = std::vector; using OutputAdapter = bitsery::OutputBufferAdapter; using InputAdapter = bitsery::InputBufferAdapter; using bitsery::ReaderError; using testing::Eq; using testing::Ge; struct DisableAdapterErrorsConfig { static constexpr bitsery::EndiannessType Endianness = bitsery::DefaultConfig::Endianness; static constexpr bool CheckAdapterErrors = false; static constexpr bool CheckDataErrors = true; }; TEST(OutputBuffer, WhenInitialBufferIsEmptyThenResizeInAdapterConstructor) { //setup data Buffer buf{}; EXPECT_THAT(buf.size(), Eq(0)); OutputAdapter adapter{buf}; EXPECT_THAT(buf.size(), Ge(1)); } TEST(OutputBuffer, WhenSetWritePositionThenResizeUnderlyingBufferIfRequired) { //setup data Buffer buf{}; OutputAdapter w{buf}; const auto initialSize = buf.size(); EXPECT_THAT(buf.size(), Eq(initialSize)); EXPECT_THAT(w.currentWritePos(), Eq(0)); w.currentWritePos(initialSize + 10); EXPECT_THAT(w.currentWritePos(), Eq(initialSize + 10)); EXPECT_THAT(buf.size(), Ge(initialSize + 10)); } TEST(OutputBuffer, WhenSettingCurrentPositionBeforeBufferEndThenWrittenBytesCountIsNotAffected) { //setup data Buffer buf{}; OutputAdapter w{buf}; const auto initialSize = buf.size(); EXPECT_THAT(buf.size(), Eq(initialSize)); EXPECT_THAT(w.writtenBytesCount(), Eq(0)); w.currentWritePos(initialSize + 10); w.writeBytes<8>(static_cast(1)); EXPECT_THAT(w.writtenBytesCount(), Eq(initialSize + 10 + 8)); w.currentWritePos(0); EXPECT_THAT(w.writtenBytesCount(), Eq(initialSize + 10 + 8)); } TEST(OutputBuffer, CanWorkWithFixedSizeBuffer) { //setup data std::array buf{}; bitsery::OutputBufferAdapter> w{buf}; const auto initialSize = buf.size(); EXPECT_THAT(buf.size(), Eq(initialSize)); EXPECT_THAT(w.currentWritePos(), Eq(0)); w.currentWritePos(5); EXPECT_THAT(w.currentWritePos(), Eq(5)); } TEST(InputBuffer, CorrectlySetsAndGetsCurrentReadPosition) { Buffer buf{}; buf.resize(100); InputAdapter r{buf.begin(), 10}; r.currentReadPos(5); EXPECT_THAT(r.currentReadPos(), Eq(5)); r.currentReadPos(0); EXPECT_THAT(r.currentReadPos(), Eq(0)); uint8_t tmp; r.readBytes<1>(tmp); EXPECT_THAT(r.currentReadPos(), Eq(1)); } TEST(InputBuffer, WhenSetReadPositionOutOfRangeThenDataOverflow) { Buffer buf{}; buf.resize(100); InputAdapter r{buf.begin(), 10}; r.currentReadPos(10); EXPECT_THAT(r.error(), Eq(ReaderError::NoError)); r.currentReadPos(11); EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow)); } TEST(InputBuffer, WhenSetReadEndPositionOutOfRangeThenDataOverflow) { Buffer buf{}; buf.resize(100); InputAdapter r{buf.begin(), 10}; r.currentReadEndPos(11); EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow)); } TEST(InputBuffer, WhenReadEndPositionIsNotSetThenReturnZeroAsBufferEndPosition) { Buffer buf{}; buf.resize(100); InputAdapter r{buf.begin(), 10}; EXPECT_THAT(r.currentReadEndPos(), Eq(0)); r.currentReadEndPos(5); EXPECT_THAT(r.currentReadEndPos(), Eq(5)); EXPECT_THAT(r.error(), Eq(ReaderError::NoError)); } TEST(InputBuffer, WhenReadEndPositionIsNotZeroThenDataOverflowErrorWillBeIgnored) { Buffer buf{}; buf.resize(100); InputAdapter r{buf.begin(), 1}; r.currentReadEndPos(1); uint32_t tmp{}; r.readBytes<4>(tmp); EXPECT_THAT(tmp, Eq(0)); EXPECT_THAT(r.error(), Eq(ReaderError::NoError)); r.currentReadEndPos(0); r.readBytes<4>(tmp); EXPECT_THAT(tmp, Eq(0)); EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow)); } TEST(InputBuffer, WhenReadingPastReadEndPositionOrBufferEndThenReadPositionDoesntChange) { Buffer buf{}; buf.resize(10); InputAdapter r{buf.begin(), 3}; uint32_t tmp{}; r.currentReadEndPos(2); r.readBytes<4>(tmp); EXPECT_THAT(r.currentReadPos(), Eq(0)); EXPECT_THAT(r.error(), Eq(ReaderError::NoError)); EXPECT_THAT(tmp, Eq(0)); r.currentReadEndPos(0); r.readBytes<4>(tmp); EXPECT_THAT(r.currentReadPos(), Eq(0)); EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow)); EXPECT_THAT(tmp, Eq(0)); } TEST(InputBuffer, WhenReaderHasErrorsThenSettingReadPosAndReadEndPosIsIgnoredAndGettingAlwaysReturnsZero) { Buffer buf{}; buf.resize(10); InputAdapter r{buf.begin(), 10}; uint32_t tmp{}; r.readBytes<4>(tmp); r.currentReadEndPos(5); EXPECT_THAT(r.currentReadPos(), Eq(4)); EXPECT_THAT(r.currentReadEndPos(), Eq(5)); EXPECT_THAT(r.error(), Eq(ReaderError::NoError)); r.currentReadEndPos(11); EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow)); EXPECT_THAT(r.currentReadPos(), Eq(0)); EXPECT_THAT(r.currentReadEndPos(), Eq(0)); r.currentReadPos(1); r.currentReadEndPos(1); EXPECT_THAT(r.currentReadPos(), Eq(0)); EXPECT_THAT(r.currentReadEndPos(), Eq(0)); } TEST(InputBuffer, ConstDataForBufferAllAdapters) { //create and write to buffer uint16_t data = 7549; Buffer bufWrite{}; OutputAdapter bw{bufWrite}; bw.writeBytes<2>(data); bw.flush(); const Buffer buf{bufWrite}; //read from buffer bitsery::InputBufferAdapter r1{buf.begin(), buf.end()}; uint16_t res1{}; r1.readBytes<2>(res1); EXPECT_THAT(res1, Eq(data)); } #ifndef NDEBUG TEST(InputBuffer, WhenAdapterErrorsIsDisabledThenCanChangeAnyReadPositionAndReadsAsserts) { //create and write to buffer uint64_t data = 0x1122334455667788; Buffer buf{}; OutputAdapter bw{buf}; bw.writeBytes<8>(data); bw.flush(); bitsery::InputBufferAdapter r1{buf.begin(), 2}; uint16_t res1{}; r1.readBytes<2>(res1); EXPECT_THAT(res1, Eq(0x7788)); // default config is little endian EXPECT_THAT(r1.currentReadPos(), Eq(2)); r1.currentReadPos(4); EXPECT_THAT(r1.currentReadPos(), Eq(4)); EXPECT_DEATH(r1.readBytes<2>(res1), ""); // default config is little endian } #endif TEST(InputStream, WhenAdapterErrorsIsDisabledThenReadingPastEndDoesntSetErrorAndDoesntReturnZero) { //create and write to buffer std::stringstream ss{}; bitsery::OutputStreamAdapter bw{ss}; uint32_t data = 0x12345678; bw.writeBytes<4>(data); bw.flush(); bitsery::BasicInputStreamAdapter> br{ss}; uint32_t res{}; br.readBytes<4>(res); EXPECT_THAT(res, Eq(data)); br.readBytes<4>(res); EXPECT_THAT(res, Eq(data)); EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true)); } template class TAdapter> struct InBufferConfig { using Data = std::vector; using Adapter = TAdapter; Data data{}; Adapter createReader(const std::vector& buffer) { data = buffer; return Adapter{data.begin(), data.size()}; } }; template struct InStreamConfig { using Data = std::stringstream; using Adapter = TAdapter; Data data{}; Adapter createReader(const std::vector& buffer) { std::string str(buffer.begin(), buffer.end()); data = std::stringstream{str}; return Adapter{data}; } }; template class AdapterConfig : public testing::Test { public: TAdapterWithData config{}; }; using AdapterInputTypes = ::testing::Types< InBufferConfig, InStreamConfig >; template class InputAll: public AdapterConfig { }; TYPED_TEST_CASE(InputAll, AdapterInputTypes); TYPED_TEST(InputAll, SettingMultipleErrorsAlwaysReturnsFirstError) { auto r = this->config.createReader({0,0,0,0}); EXPECT_THAT(r.error(), Eq(ReaderError::NoError)); r.error(ReaderError::InvalidPointer); EXPECT_THAT(r.error(), Eq(ReaderError::InvalidPointer)); r.error(ReaderError::DataOverflow); EXPECT_THAT(r.error(), Eq(ReaderError::InvalidPointer)); r.error(ReaderError::NoError); EXPECT_THAT(r.error(), Eq(ReaderError::InvalidPointer)); } TYPED_TEST(InputAll, CanBeMoveConstructedAndMoveAssigned) { auto r = this->config.createReader({1,2,3}); uint8_t res{}; r.template readBytes<1>(res); EXPECT_THAT(res, Eq(1)); // move construct auto r1 = std::move(r); r1.template readBytes<1>(res); EXPECT_THAT(res, Eq(2)); // move assign r = std::move(r1); r.template readBytes<1>(res); EXPECT_THAT(res, Eq(3)); } TYPED_TEST(InputAll, WhenAlignHasNonZerosThenInvalidDataError) { auto r = this->config.createReader({0x7F}); bitsery::details::InputAdapterBitPackingWrapper bpr{r}; uint8_t tmp{0xFF}; bpr.readBits(tmp,3); bpr.align(); EXPECT_THAT(bpr.error(), Eq(ReaderError::InvalidData)); } TYPED_TEST(InputAll, WhenAllBytesAreReadWithoutErrorsThenIsCompletedSuccessfully) { //setup data uint32_t tb = 94545646; int16_t tc = -8778; uint8_t td = 200; //create and write to buffer Buffer buf{}; OutputAdapter bw{buf}; bw.writeBytes<4>(tb); bw.writeBytes<2>(tc); bw.writeBytes<1>(td); bw.flush(); buf.resize(bw.writtenBytesCount()); auto br = this->config.createReader(buf); uint32_t rb = 94545646; int16_t rc = -8778; uint8_t rd = 200; br.template readBytes<4>(rb); EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError)); br.template readBytes<2>(rc); EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError)); EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false)); br.template readBytes<1>(rd); EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError)); EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true)); EXPECT_THAT(rb, Eq(tb)); EXPECT_THAT(rc, Eq(tc)); EXPECT_THAT(rd, Eq(td)); } TYPED_TEST(InputAll, WhenReadingMoreThanAvailableThenDataOverflow) { //setup data uint8_t t1 = 111; Buffer buf{}; OutputAdapter w{buf}; w.writeBytes<1>(t1); w.flush(); buf.resize(w.writtenBytesCount()); auto r = this->config.createReader(buf); uint8_t r1{}; EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false)); EXPECT_THAT(r.error(), Eq(ReaderError::NoError)); r.template readBytes<1>(r1); EXPECT_THAT(r.isCompletedSuccessfully(), Eq(true)); EXPECT_THAT(r.error(), Eq(ReaderError::NoError)); EXPECT_THAT(r1, Eq(t1)); r.template readBytes<1>(r1); r.template readBytes<1>(r1); EXPECT_THAT(r1, Eq(0)); EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false)); EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow)); } TYPED_TEST(InputAll, WhenReaderHasErrorsAllThenReadsReturnZero) { //setup data uint8_t t1 = 111; Buffer buf{}; OutputAdapter w{buf}; w.writeBytes<1>(t1); w.writeBytes<1>(t1); w.flush(); buf.resize(w.writtenBytesCount()); auto r = this->config.createReader(buf); uint8_t r1{}; r.template readBytes<1>(r1); EXPECT_THAT(r1, Eq(t1)); r.error(ReaderError::InvalidPointer); r.template readBytes<1>(r1); EXPECT_THAT(r1, Eq(0)); } template class TAdapter> struct OutBufferConfig { using Data = std::vector; using Adapter = TAdapter; Data data{}; Adapter createWriter() { return Adapter{data}; } bitsery::InputBufferAdapter getReader() { return bitsery::InputBufferAdapter{data.begin(), data.end()}; } }; template struct OutStreamConfig { using Data = std::stringstream; using Adapter = TAdapter; Data data{}; Adapter createWriter() { return Adapter{data}; } bitsery::InputStreamAdapter getReader() { return bitsery::InputStreamAdapter{data}; } }; using AdapterOutputTypes = ::testing::Types< OutBufferConfig, OutStreamConfig, OutStreamConfig >; template class OutputAll: public AdapterConfig { }; TYPED_TEST_CASE(OutputAll, AdapterOutputTypes); TYPED_TEST(OutputAll, CanBeMoveConstructedAndMoveAssigned) { auto w = this->config.createWriter(); uint8_t data{1}; w.template writeBytes<1>(data); // move construct auto w1 = std::move(w); data = 2; w1.template writeBytes<1>(data); // move assignment w = std::move(w1); data = 3; w.template writeBytes<1>(data); w.flush(); auto r = this->config.getReader(); r.template readBytes<1>(data); EXPECT_THAT(data, Eq(1)); r.template readBytes<1>(data); EXPECT_THAT(data, Eq(2)); r.template readBytes<1>(data); EXPECT_THAT(data, Eq(3)); } template class OutputStreamBuffered : public testing::Test { public: using Buffer = T; using Adapter = bitsery::BasicBufferedOutputStreamAdapter, Buffer>; static constexpr size_t InternalBufferSize = 128; std::stringstream stream{}; Adapter writer{stream, 128}; }; using BufferedAdapterInternalBufferTypes = ::testing::Types< std::vector, std::array, std::string >; TYPED_TEST_CASE(OutputStreamBuffered, BufferedAdapterInternalBufferTypes); TYPED_TEST(OutputStreamBuffered, WhenInternalBufferIsFullThenWriteBufferAndRemainingDataToStream) { uint8_t x{}; for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i) this->writer.template writeBytes<1>(x); EXPECT_TRUE(this->stream.str().empty()); this->writer.template writeBytes<1>(x); EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize + 1)); } TYPED_TEST(OutputStreamBuffered, WhenFlushThenWriteImmediately) { uint8_t x{}; this->writer.template writeBytes<1>(x); EXPECT_THAT(this->stream.str().size(), Eq(0)); this->writer.flush(); EXPECT_THAT(this->stream.str().size(), Eq(1)); this->writer.flush(); EXPECT_THAT(this->stream.str().size(), Eq(1)); } TYPED_TEST(OutputStreamBuffered, WhenBufferIsStackAllocatedThenBufferSizeViaCtorHasNoEffect) { //create writer with half the internal buffer size //for std::vector it should overflow, and for std::array it should have no effect typename TestFixture::Adapter w{this->stream, TestFixture::InternalBufferSize / 2}; uint8_t x{}; for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i) w.template writeBytes<1>(x); static constexpr bool ShouldWriteToStream = bitsery::traits::ContainerTraits::isResizable; EXPECT_THAT(this->stream.str().empty(), ::testing::Ne(ShouldWriteToStream)); } TEST(AdapterWriterMeasureSize, CorrectlyMeasuresWrittenBytesCountForSerialization) { bitsery::MeasureSize w{}; EXPECT_THAT(w.writtenBytesCount(), Eq(0)); w.writeBytes<8>(uint64_t{0}); EXPECT_THAT(w.writtenBytesCount(), Eq(8)); w.writeBuffer<8, uint64_t>(nullptr, 9); EXPECT_THAT(w.writtenBytesCount(), Eq(80)); w.currentWritePos(10); w.writeBytes<4>(uint32_t{0}); EXPECT_THAT(w.writtenBytesCount(), Eq(80)); EXPECT_THAT(w.currentWritePos(), Eq(14)); w.currentWritePos(80); EXPECT_THAT(w.writtenBytesCount(), Eq(80)); w.writeBits(uint32_t{0}, 7u); EXPECT_THAT(w.writtenBytesCount(), Eq(80)); w.align(); EXPECT_THAT(w.writtenBytesCount(), Eq(81)); w.writeBits(uint32_t{0}, 7u); w.flush(); EXPECT_THAT(w.writtenBytesCount(), Eq(82)); // doesn't compile on older compilers if I write bitsery::MeasureSize::BitPackingEnabled directly in EXPECT_THAT macro. constexpr bool bpEnabled = bitsery::MeasureSize::BitPackingEnabled; EXPECT_THAT(bpEnabled, Eq(true)); }