| // |
| // Copyright 2023 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // CircularBuffer_unittest: |
| // Tests of the CircularBuffer class |
| // |
| |
| #include <gtest/gtest.h> |
| |
| #include "common/FixedQueue.h" |
| |
| #include <chrono> |
| #include <thread> |
| |
| namespace angle |
| { |
| // Make sure the various constructors compile and do basic checks |
| TEST(FixedQueue, Constructors) |
| { |
| FixedQueue<int, 5> q; |
| EXPECT_EQ(0u, q.size()); |
| EXPECT_EQ(true, q.empty()); |
| } |
| |
| // Make sure the destructor destroys all elements. |
| TEST(FixedQueue, Destructor) |
| { |
| struct s |
| { |
| s() : counter(nullptr) {} |
| s(int *c) : counter(c) {} |
| ~s() |
| { |
| if (counter) |
| { |
| ++*counter; |
| } |
| } |
| |
| s(const s &) = default; |
| s &operator=(const s &) = default; |
| |
| int *counter; |
| }; |
| |
| int destructorCount = 0; |
| |
| { |
| FixedQueue<s, 11> q; |
| q.push(s(&destructorCount)); |
| // Destructor called once for the temporary above. |
| EXPECT_EQ(1, destructorCount); |
| } |
| |
| // Destructor should be called one more time for the element we pushed. |
| EXPECT_EQ(2, destructorCount); |
| } |
| |
| // Make sure the pop destroys the element. |
| TEST(FixedQueue, Pop) |
| { |
| struct s |
| { |
| s() : counter(nullptr) {} |
| s(int *c) : counter(c) {} |
| ~s() |
| { |
| if (counter) |
| { |
| ++*counter; |
| } |
| } |
| |
| s(const s &) = default; |
| s &operator=(const s &s) |
| { |
| // increment if we are overwriting the custom initialized object |
| if (counter) |
| { |
| ++*counter; |
| } |
| counter = s.counter; |
| return *this; |
| } |
| |
| int *counter; |
| }; |
| |
| int destructorCount = 0; |
| |
| FixedQueue<s, 11> q; |
| q.push(s(&destructorCount)); |
| // Destructor called once for the temporary above. |
| EXPECT_EQ(1, destructorCount); |
| q.pop(); |
| // Copy assignment should be called for the element we popped. |
| EXPECT_EQ(2, destructorCount); |
| } |
| |
| // Test circulating behavior. |
| TEST(FixedQueue, WrapAround) |
| { |
| FixedQueue<int, 7> q; |
| |
| for (int i = 0; i < 7; ++i) |
| { |
| q.push(i); |
| } |
| |
| EXPECT_EQ(0, q.front()); |
| q.pop(); |
| // This should wrap around |
| q.push(7); |
| for (int i = 0; i < 7; ++i) |
| { |
| EXPECT_EQ(i + 1, q.front()); |
| q.pop(); |
| } |
| } |
| |
| // Test concurrent push and pop behavior. |
| TEST(FixedQueue, ConcurrentPushPop) |
| { |
| FixedQueue<uint64_t, 7> q; |
| double timeOut = 1.0; |
| uint64_t kMaxLoop = 1000000ull; |
| std::thread enqueueThread = std::thread([&]() { |
| std::time_t t1 = std::time(nullptr); |
| uint64_t value = 0; |
| do |
| { |
| while (q.full()) |
| { |
| std::this_thread::sleep_for(std::chrono::microseconds(1)); |
| } |
| q.push(value); |
| value++; |
| } while (difftime(std::time(nullptr), t1) < timeOut && value < kMaxLoop); |
| ASSERT(difftime(std::time(nullptr), t1) >= timeOut || value >= kMaxLoop); |
| }); |
| |
| std::thread dequeueThread = std::thread([&]() { |
| std::time_t t1 = std::time(nullptr); |
| uint64_t expectedValue = 0; |
| do |
| { |
| while (q.empty()) |
| { |
| std::this_thread::sleep_for(std::chrono::microseconds(1)); |
| } |
| |
| EXPECT_EQ(expectedValue, q.front()); |
| // test pop |
| q.pop(); |
| |
| expectedValue++; |
| } while (difftime(std::time(nullptr), t1) < timeOut && expectedValue < kMaxLoop); |
| ASSERT(difftime(std::time(nullptr), t1) >= timeOut || expectedValue >= kMaxLoop); |
| }); |
| |
| enqueueThread.join(); |
| dequeueThread.join(); |
| } |
| |
| // Test clearing the queue |
| TEST(FixedQueue, Clear) |
| { |
| FixedQueue<int, 5> q; |
| for (int i = 0; i < 5; ++i) |
| { |
| q.push(i); |
| } |
| q.clear(); |
| EXPECT_EQ(0u, q.size()); |
| EXPECT_EQ(true, q.empty()); |
| } |
| } // namespace angle |