/* * * Copyright (c) 2021 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include namespace { using namespace chip; class TestBytesCircularBuffer : public ::testing::Test { public: static void SetUpTestSuite() { unsigned seed = static_cast(std::time(nullptr)); printf("Running " __FILE__ " using seed %d \n", seed); std::srand(seed); } }; TEST_F(TestBytesCircularBuffer, TestPushInvalid) { uint8_t storage[10]; BytesCircularBuffer buffer(storage, sizeof(storage)); const uint8_t s[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; EXPECT_TRUE(buffer.IsEmpty()); EXPECT_EQ(buffer.Push(ByteSpan(s, static_cast(-1))), CHIP_ERROR_INVALID_ARGUMENT); EXPECT_TRUE(buffer.IsEmpty()); EXPECT_EQ(buffer.Push(ByteSpan(s, 8)), CHIP_ERROR_INVALID_ARGUMENT); EXPECT_TRUE(buffer.IsEmpty()); EXPECT_EQ(buffer.Push(ByteSpan(s, 9)), CHIP_ERROR_INVALID_ARGUMENT); EXPECT_TRUE(buffer.IsEmpty()); EXPECT_EQ(buffer.Push(ByteSpan(s, 7)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); } void RandomPushPop(BytesCircularBuffer & buffer, std::list> & alreadyInBuffer) { auto randomString = [] { std::vector str(static_cast(std::rand()) % 8); std::generate_n(str.begin(), str.size(), [] { return std::rand(); }); return str; }; auto bufferSize = [&alreadyInBuffer] { return std::accumulate(alreadyInBuffer.begin(), alreadyInBuffer.end(), 0u, [](auto s, auto str) { return s + str.size() + 2; }); }; auto verify = [&alreadyInBuffer, &buffer] { if (alreadyInBuffer.empty()) { EXPECT_TRUE(buffer.IsEmpty()); } else { EXPECT_FALSE(buffer.IsEmpty()); auto length = alreadyInBuffer.front().size(); EXPECT_EQ(buffer.GetFrontSize(), length); std::vector str(length); MutableByteSpan readSpan(str.data(), length); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(readSpan.size(), length); EXPECT_EQ(alreadyInBuffer.front(), str); } }; verify(); // Run 200 random operations for (int i = 0; i < 200; ++i) { if (!buffer.IsEmpty() && std::rand() % 3 == 0) { // pop EXPECT_EQ(buffer.Pop(), CHIP_NO_ERROR); alreadyInBuffer.pop_front(); verify(); } else { // push random string auto str = randomString(); EXPECT_EQ(buffer.Push(ByteSpan(str.data(), str.size())), CHIP_NO_ERROR); alreadyInBuffer.push_back(str); while (bufferSize() > 9) { alreadyInBuffer.pop_front(); } verify(); } } } TEST_F(TestBytesCircularBuffer, TestPushPopRandom) { uint8_t storage[10]; BytesCircularBuffer buffer(storage, sizeof(storage)); EXPECT_TRUE(buffer.IsEmpty()); std::list> alreadyInBuffer; RandomPushPop(buffer, alreadyInBuffer); } TEST_F(TestBytesCircularBuffer, TestPushToJustFull) { uint8_t storage[10]; // Total space = 9 BytesCircularBuffer buffer(storage, sizeof(storage)); EXPECT_TRUE(buffer.IsEmpty()); const uint8_t s1[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; const uint8_t s2[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; uint8_t o[sizeof(s1)]; MutableByteSpan readSpan(o); EXPECT_TRUE(buffer.IsEmpty()); // Used = 6, Free = 3 EXPECT_EQ(buffer.Push(ByteSpan(s1, 4)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 4u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_FALSE(memcmp(s1, readSpan.data(), 4)); // Used = 9, Free = 0 EXPECT_EQ(buffer.Push(ByteSpan(s2, 1)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 4u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_FALSE(memcmp(s1, readSpan.data(), 4)); // Try normal push/pop again std::list> alreadyInBuffer; alreadyInBuffer.push_back(std::vector{ s1[0], s1[1], s1[2], s1[3] }); alreadyInBuffer.push_back(std::vector{ s2[0] }); RandomPushPop(buffer, alreadyInBuffer); } TEST_F(TestBytesCircularBuffer, TestPushToJustFullOverOne) { uint8_t storage[10]; // Total space = 9 BytesCircularBuffer buffer(storage, sizeof(storage)); EXPECT_TRUE(buffer.IsEmpty()); const uint8_t s1[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; const uint8_t s2[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; uint8_t o[sizeof(s1)]; MutableByteSpan readSpan(o); EXPECT_TRUE(buffer.IsEmpty()); // Used = 6, Free = 3 EXPECT_EQ(buffer.Push(ByteSpan(s1, 4)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 4u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s1, readSpan.data(), 4), 0); // Used = 4, Free = 5 EXPECT_EQ(buffer.Push(ByteSpan(s2, 2)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 2u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s2, readSpan.data(), 2), 0); // Try normal push/pop again std::list> alreadyInBuffer; alreadyInBuffer.push_back(std::vector{ s2[0], s2[1] }); RandomPushPop(buffer, alreadyInBuffer); } TEST_F(TestBytesCircularBuffer, TestPushWrap) { uint8_t storage[10]; // Total space = 9 BytesCircularBuffer buffer(storage, sizeof(storage)); EXPECT_TRUE(buffer.IsEmpty()); const uint8_t s1[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; const uint8_t s2[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; uint8_t o[sizeof(s1)]; MutableByteSpan readSpan(o); EXPECT_TRUE(buffer.IsEmpty()); // Used = 6, Free = 3 EXPECT_EQ(buffer.Push(ByteSpan(s1, 4)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 4u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s1, readSpan.data(), 4), 0); // Used = 6, Free = 3, s1 was discarded due to lack of storage EXPECT_EQ(buffer.Push(ByteSpan(s2, 4)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 4u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s2, readSpan.data(), 4), 0); // Used = 9, Free = 0 EXPECT_EQ(buffer.Push(ByteSpan(s1, 1)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 4u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s2, readSpan.data(), 4), 0); // Used = 3, Free = 6 EXPECT_EQ(buffer.Pop(), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_TRUE(buffer.GetFrontSize()); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s1, readSpan.data(), 1), 0); // All freed EXPECT_EQ(buffer.Pop(), CHIP_NO_ERROR); EXPECT_TRUE(buffer.IsEmpty()); } TEST_F(TestBytesCircularBuffer, TestPushWrapStartAtEdge) { uint8_t storage[10]; const uint8_t s1[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; const uint8_t s2[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; uint8_t o[sizeof(s1)]; MutableByteSpan readSpan(o); // Total space = 9 BytesCircularBuffer buffer(storage, sizeof(storage)); EXPECT_TRUE(buffer.IsEmpty()); // Used = 6, Free = 3, mDataStart = 0, mDataEnd = 6 EXPECT_EQ(buffer.Push(ByteSpan(s1, 4)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 4u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s1, readSpan.data(), 4), 0); // Used = 4, Free = 5, mDataStart = 6, mDataEnd = 0 wrap mDataEnd EXPECT_EQ(buffer.Push(ByteSpan(s2, 2)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 2u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s2, readSpan.data(), 2), 0); // Used = 7, Free = 2, mDataStart = 6, mDataEnd = 3 wrap mDataEnd EXPECT_EQ(buffer.Push(ByteSpan(s1, 1)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 2u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s2, readSpan.data(), 2), 0); // Used = 3, Free = 6, mDataStart = 0, mDataEnd = 3 EXPECT_EQ(buffer.Pop(), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 1u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s1, readSpan.data(), 1), 0); // Try normal push/pop again std::list> alreadyInBuffer; alreadyInBuffer.push_back(std::vector{ s1[0] }); RandomPushPop(buffer, alreadyInBuffer); } TEST_F(TestBytesCircularBuffer, TestPushWrapEndAtEdge) { uint8_t storage[10]; const uint8_t s1[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; const uint8_t s2[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; uint8_t o[sizeof(s1)]; MutableByteSpan readSpan(o); // Total space = 9 BytesCircularBuffer buffer(storage, sizeof(storage)); EXPECT_TRUE(buffer.IsEmpty()); // Used = 6, Free = 3, mDataStart = 0, mDataEnd = 6 EXPECT_EQ(buffer.Push(ByteSpan(s1, 4)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 4u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s1, readSpan.data(), 4), 0); // Used = 4, Free = 5, mDataStart = 6, mDataEnd = 0 wrap mDataEnd EXPECT_EQ(buffer.Push(ByteSpan(s2, 2)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 2u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s2, readSpan.data(), 2), 0); // Try normal push/pop again std::list> alreadyInBuffer; alreadyInBuffer.push_back(std::vector{ s2[0], s2[1] }); RandomPushPop(buffer, alreadyInBuffer); } TEST_F(TestBytesCircularBuffer, TestPushWhenFull) { uint8_t storage[10]; const uint8_t s1[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; const uint8_t s2[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; uint8_t o[sizeof(s1)]; MutableByteSpan readSpan(o); // Total space = 9 BytesCircularBuffer buffer(storage, sizeof(storage)); EXPECT_TRUE(buffer.IsEmpty()); // Used = 9, Free = 0 EXPECT_EQ(buffer.Push(ByteSpan(s1, 7)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 7u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s1, readSpan.data(), 7), 0); // Used = 5, Free = 4 EXPECT_EQ(buffer.Push(ByteSpan(s2, 3)), CHIP_NO_ERROR); EXPECT_FALSE(buffer.IsEmpty()); EXPECT_EQ(buffer.GetFrontSize(), 3u); readSpan = MutableByteSpan(o); EXPECT_EQ(buffer.ReadFront(readSpan), CHIP_NO_ERROR); EXPECT_EQ(memcmp(s2, readSpan.data(), 3), 0); // All freed EXPECT_EQ(buffer.Pop(), CHIP_NO_ERROR); EXPECT_TRUE(buffer.IsEmpty()); // Try normal push/pop again std::list> alreadyInBuffer; RandomPushPop(buffer, alreadyInBuffer); } } // namespace