// Copyright (c) 2019 - 2020 by Robert Bosch GmbH. All rights reserved. // Copyright (c) 2020 - 2021 by Apex.AI Inc. 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. // // SPDX-License-Identifier: Apache-2.0 #include "test.hpp" #include "iceoryx_hoofs/internal/concurrent/lockfree_queue/index_queue.hpp" namespace { using namespace ::testing; using iox::concurrent::IndexQueue; template class IndexQueueTest : public ::testing::Test { public: using Queue = T; using index_t = typename Queue::value_t; protected: IndexQueueTest() { } ~IndexQueueTest() { } void SetUp() { } void TearDown() { } Queue queue; Queue fullQueue{Queue::ConstructFull}; }; TEST(LockFreeQueueTest, capacityIsConsistent) { ::testing::Test::RecordProperty("TEST_ID", "86d94598-7271-45a1-a6c9-afad0bc8cc8b"); IndexQueue<37U> q; EXPECT_EQ(q.capacity(), 37U); } typedef ::testing::Types, IndexQueue<10>, IndexQueue<1000>> TestQueues; TYPED_TEST_SUITE(IndexQueueTest, TestQueues); TYPED_TEST(IndexQueueTest, defaultConstructedQueueIsEmpty) { ::testing::Test::RecordProperty("TEST_ID", "7fde1929-62b9-4377-8d02-5bcdd18c246f"); auto& q = this->queue; EXPECT_TRUE(q.empty()); } TYPED_TEST(IndexQueueTest, constructedQueueIsEmpty) { ::testing::Test::RecordProperty("TEST_ID", "e38bb9be-720b-4aba-a17a-f7bdaa34164a"); using Queue = typename TestFixture::Queue; Queue q(Queue::ConstructEmpty); EXPECT_TRUE(q.empty()); } TYPED_TEST(IndexQueueTest, queueIsNotEmptyAfterPush) { ::testing::Test::RecordProperty("TEST_ID", "e9150ab1-ca01-44bf-b0bb-eab242c3a73f"); auto& q = this->queue; auto index = this->fullQueue.pop(); q.push(index.value()); EXPECT_FALSE(q.empty()); } TYPED_TEST(IndexQueueTest, queueIsEmptyAgainAfterPushFollowedByPop) { ::testing::Test::RecordProperty("TEST_ID", "38c6eba6-4c21-4df1-9f47-f3bf9438b5b1"); auto& q = this->queue; auto index = this->fullQueue.pop(); q.push(index.value()); EXPECT_FALSE(q.empty()); q.pop(); EXPECT_TRUE(q.empty()); } TYPED_TEST(IndexQueueTest, IndicesAreIncreasingWhenConstructedFull) { ::testing::Test::RecordProperty("TEST_ID", "b83d43f5-fa30-417e-aa2e-3a69408e7869"); using Queue = typename TestFixture::Queue; using index_t = typename TestFixture::index_t; Queue& q = this->fullQueue; EXPECT_FALSE(q.empty()); index_t expected{0U}; auto index = q.pop(); while (index.has_value()) { EXPECT_EQ(index.value(), expected++); index = q.pop(); } } TYPED_TEST(IndexQueueTest, queueIsNotEmptyWhenConstructedFull) { ::testing::Test::RecordProperty("TEST_ID", "6a4ee1da-e70b-49d8-bfbe-0ef6d4aacf8c"); using Queue = typename TestFixture::Queue; Queue& q = this->fullQueue; EXPECT_FALSE(q.empty()); } TYPED_TEST(IndexQueueTest, queueIsEmptyWhenPopFails) { ::testing::Test::RecordProperty("TEST_ID", "af8bc70d-b0d3-48ff-8322-17722e01a490"); using Queue = typename TestFixture::Queue; Queue& q = this->fullQueue; EXPECT_FALSE(q.empty()); auto index = q.pop(); while (index.has_value()) { index = q.pop(); } EXPECT_TRUE(q.empty()); } TYPED_TEST(IndexQueueTest, pushAndPopSingleElement) { ::testing::Test::RecordProperty("TEST_ID", "983b068f-2847-4813-bf19-3ae713261576"); using index_t = typename TestFixture::index_t; auto& q = this->queue; auto index = this->fullQueue.pop(); index_t rawIndex = index.value(); EXPECT_TRUE(index.has_value()); q.push(index.value()); auto popped = q.pop(); ASSERT_TRUE(popped.has_value()); EXPECT_EQ(popped.value(), rawIndex); } TYPED_TEST(IndexQueueTest, poppedElementsAreInFifoOrder) { ::testing::Test::RecordProperty("TEST_ID", "7004d51a-9a2f-4ac9-90bc-bb755b393431"); auto& q = this->queue; using index_t = typename TestFixture::index_t; auto capacity = q.capacity(); index_t expected{0U}; for (uint64_t i = 0U; i < capacity; ++i) { auto index = this->fullQueue.pop(); EXPECT_EQ(index.value(), expected++); q.push(index.value()); } expected = 0; for (uint64_t i = 0U; i < capacity; ++i) { auto popped = q.pop(); ASSERT_TRUE(popped.has_value()); EXPECT_EQ(popped.value(), expected++); } } TYPED_TEST(IndexQueueTest, popReturnsNothingWhenQueueIsEmpty) { ::testing::Test::RecordProperty("TEST_ID", "20487320-1c70-4212-aa00-847105780a71"); auto& q = this->queue; EXPECT_FALSE(q.pop().has_value()); } TYPED_TEST(IndexQueueTest, popIfFullReturnsNothingWhenQueueIsEmpty) { ::testing::Test::RecordProperty("TEST_ID", "68773e9d-bc67-4929-ba4d-05197bbdfcac"); auto& q = this->queue; EXPECT_FALSE(q.popIfFull().has_value()); } TYPED_TEST(IndexQueueTest, popIfFullReturnsOldestElementWhenQueueIsFull) { ::testing::Test::RecordProperty("TEST_ID", "a4479ef7-a19d-4f4d-a1af-d2abb8f6fa0c"); using Queue = typename TestFixture::Queue; Queue& q = this->fullQueue; auto index = q.popIfFull(); EXPECT_TRUE(index.has_value()); EXPECT_EQ(index.value(), 0U); } TYPED_TEST(IndexQueueTest, popIfFullReturnsNothingWhenQueueIsNotFull) { ::testing::Test::RecordProperty("TEST_ID", "932a6096-4b56-42fc-8f9d-06a9fcf4c7d3"); using Queue = typename TestFixture::Queue; Queue& q = this->fullQueue; auto index = q.pop(); EXPECT_TRUE(index.has_value()); EXPECT_FALSE(q.popIfFull().has_value()); } TYPED_TEST(IndexQueueTest, popIfSizeIsAtLeastReturnsNothingIfQueueIsEmpty) { ::testing::Test::RecordProperty("TEST_ID", "415c5f5c-fc0d-45e3-994d-4ce7af9a9e98"); auto& q = this->queue; EXPECT_FALSE(q.popIfSizeIsAtLeast(1U).has_value()); } TYPED_TEST(IndexQueueTest, popIfSizeIsAtLeastZeroReturnsIndexIfQueueIsFull) { ::testing::Test::RecordProperty("TEST_ID", "1cb99149-8a34-43bf-ab6b-3744cb18889e"); auto& q = this->fullQueue; EXPECT_TRUE(q.popIfSizeIsAtLeast(0U).has_value()); } TYPED_TEST(IndexQueueTest, popIfSizeIsAtLeastZeroReturnsNothingIfQueueIsEmpty) { ::testing::Test::RecordProperty("TEST_ID", "74ba9355-4ec6-49ce-b4fc-d01b820d9bd1"); auto& q = this->queue; EXPECT_FALSE(q.popIfSizeIsAtLeast(0U).has_value()); } TYPED_TEST(IndexQueueTest, popIfSizeIsAtLeastZeroReturnsIndexIfQueueContainsOneElement) { ::testing::Test::RecordProperty("TEST_ID", "fb58c76c-1bd0-4a5b-8732-63832f3a7fd9"); auto& q = this->queue; auto index = this->fullQueue.pop(); ASSERT_TRUE(index.has_value()); q.push(*index); index = q.popIfSizeIsAtLeast(0U); ASSERT_TRUE(index.has_value()); } TYPED_TEST(IndexQueueTest, popIfSizeIsAtLeastOneReturnsIndexIfQueueContainsOneElement) { ::testing::Test::RecordProperty("TEST_ID", "d97a588c-c847-401e-82b6-db58f791fbf4"); auto& q = this->queue; using index_t = typename TestFixture::index_t; // we can only push indices up to capacity - 1 const index_t expectedIndex{q.capacity() - 1}; q.push(expectedIndex); const auto index = q.popIfSizeIsAtLeast(1U); ASSERT_TRUE(index.has_value()); EXPECT_EQ(*index, expectedIndex); } TYPED_TEST(IndexQueueTest, popIfSizeIsAtLeastTwoReturnsNothingIfQueueContainsOneElement) { ::testing::Test::RecordProperty("TEST_ID", "6338b9d1-235f-4af3-9eca-3daeca25f824"); auto& q = this->queue; q.push(0U); const auto index = q.popIfSizeIsAtLeast(2U); ASSERT_FALSE(index.has_value()); } TYPED_TEST(IndexQueueTest, popIfSizeIsAtLeastCapacityReturnsIndexIfQueueIsFull) { ::testing::Test::RecordProperty("TEST_ID", "5582c206-7c42-4ce3-821d-9e01085e72c5"); const auto c = this->fullQueue.capacity(); const auto index = this->fullQueue.popIfSizeIsAtLeast(c); ASSERT_TRUE(index.has_value()); EXPECT_EQ(*index, 0U); } TYPED_TEST(IndexQueueTest, popIfSizeIsAtLeastCapacityReturnsNothingIfQueueIsNotFull) { ::testing::Test::RecordProperty("TEST_ID", "d4c944bd-56d9-4dea-9d27-b346cf42e46c"); const auto CAP = this->fullQueue.capacity(); this->fullQueue.pop(); const auto index = this->fullQueue.popIfSizeIsAtLeast(CAP); ASSERT_FALSE(index.has_value()); } } // namespace