aboutsummaryrefslogtreecommitdiff
path: root/externals/oboe/samples/RhythmGame/test/testLockFreeQueue.cpp
diff options
context:
space:
mode:
authorswurl <swurl@swurl.xyz>2025-05-31 02:33:02 -0400
committerswurl <swurl@swurl.xyz>2025-05-31 02:33:02 -0400
commit6c655321e6b04b8648802e296bb5ca11d290ae68 (patch)
tree5c906511e4a2910ce8b806bb9d763b524c28619e /externals/oboe/samples/RhythmGame/test/testLockFreeQueue.cpp
parentc0cceff365f754f8859f4c9a0362ebdba7ea388b (diff)
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
Diffstat (limited to 'externals/oboe/samples/RhythmGame/test/testLockFreeQueue.cpp')
-rw-r--r--externals/oboe/samples/RhythmGame/test/testLockFreeQueue.cpp210
1 files changed, 210 insertions, 0 deletions
diff --git a/externals/oboe/samples/RhythmGame/test/testLockFreeQueue.cpp b/externals/oboe/samples/RhythmGame/test/testLockFreeQueue.cpp
new file mode 100644
index 0000000000..1dd3ac8f4c
--- /dev/null
+++ b/externals/oboe/samples/RhythmGame/test/testLockFreeQueue.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 <thread>
+#include <iostream>
+
+#include <gtest/gtest.h>
+#include "utils/LockFreeQueue.h"
+
+/**
+ * Tests
+ * =====
+ *
+ * READ/POP:
+ * - if queue is empty return false
+ * - if queue has elements return true
+ * - returns correct value once
+ * - returns correct value more than once
+ *
+ * WRITE/PUSH:
+ * - if queue is full return false
+ * - if queue has space return true
+ *
+ * WRAP:
+ * - read from full queue, then write one more element, then correct read
+ *
+ * PEEK:
+ * - peek once
+ * - peek twice
+ *
+ * SIZE:
+ * - correct zero
+ * - correct once
+ * - correct many
+ *
+ * MULTI-THREADED:
+ * - write and read concurrently, queue is empty at end and item order is maintained
+ *
+ * CANNOT TEST:
+ * - read/writes correct after wraparound (takes too long to run)
+ * - Constructing queue with non power of 2 capacity results in assert failure (needs death test)
+ * - Constructing queue with signed type results in assert failure (needs death test)
+ *
+ */
+
+constexpr int kQueueCapacity = 2;
+
+class TestLockFreeQueue : public ::testing::Test {
+
+public:
+
+ int result1;
+ int result2;
+ int result3;
+ LockFreeQueue<int, kQueueCapacity> q;
+};
+
+TEST_F(TestLockFreeQueue, PopReturnsFalseWhenEmpty){
+ bool result = q.pop(result1);
+ ASSERT_EQ(result, false);
+}
+
+TEST_F(TestLockFreeQueue, PopReturnsTrueWhenNotEmpty){
+ q.push(123);
+ bool result = q.pop(result1);
+ ASSERT_EQ(result, true);
+}
+
+TEST_F(TestLockFreeQueue, PopReturnsOneCorrectValue){
+ q.push(123);
+ q.pop(result1);
+ ASSERT_EQ(result1, 123);
+}
+
+TEST_F(TestLockFreeQueue, PopReturnsManyCorrectValues){
+ q.push(123);
+ q.push(456);
+ q.pop(result1);
+ q.pop(result2);
+ ASSERT_EQ(result1, 123);
+ ASSERT_EQ(result2, 456);
+}
+
+TEST_F(TestLockFreeQueue, PushWhenFullReturnsFalse){
+ q.push(123);
+ q.push(123);
+ ASSERT_EQ(q.push(123), false);
+}
+
+TEST_F(TestLockFreeQueue, PushWhenSpaceAvailableReturnsTrue){
+ ASSERT_EQ(q.push(123), true);
+}
+
+TEST_F(TestLockFreeQueue, PushHandlesWrapCorrectly){
+ q.push(123);
+ q.push(234);
+ q.pop(result1);
+ q.push(999);
+ q.pop(result2);
+ ASSERT_EQ(result2, 234);
+ q.pop(result3);
+ ASSERT_EQ(result3, 999);
+}
+
+TEST_F(TestLockFreeQueue, PeekWhenEmptyReturnsFalse){
+ ASSERT_EQ(q.peek(result1), false);
+}
+
+TEST_F(TestLockFreeQueue, PeekWhenNotEmptyReturnsTrue){
+ q.push(123);
+ ASSERT_EQ(q.peek(result1), true);
+}
+
+TEST_F(TestLockFreeQueue, PeekReturnsCorrectValueOnce){
+ q.push(456);
+ q.peek(result1);
+ ASSERT_EQ(result1, 456);
+}
+
+TEST_F(TestLockFreeQueue, PeekReturnsCorrectValueTwice){
+ q.push(456);
+ q.peek(result1);
+ q.peek(result2);
+ ASSERT_EQ(result2, 456);
+}
+
+TEST_F(TestLockFreeQueue, SizeReturnsZeroAfterInit){
+ ASSERT_EQ(q.size(), 0);
+}
+
+TEST_F(TestLockFreeQueue, SizeIsOneAfterPushOnce){
+ q.push(321);
+ ASSERT_EQ(q.size(), 1);
+}
+
+TEST_F(TestLockFreeQueue, SizeIsCorrectAfterPushingMaxItems){
+ for (int i = 0; i < kQueueCapacity; ++i) { q.push(i); }
+ ASSERT_EQ(q.size(), kQueueCapacity);
+}
+
+TEST_F(TestLockFreeQueue, SizeCorrectAfterWriteCounterWraps){
+
+ const uint32_t kCapacity = (uint32_t) 1 << 7;
+ LockFreeQueue<int, kCapacity, uint8_t> myQ;
+
+ for (int i = 0; i < UINT8_MAX; ++i) {
+ myQ.push(i);
+ myQ.pop(result1);
+ }
+
+ myQ.push(1);
+ ASSERT_EQ(myQ.size(), 1);
+}
+
+TEST_F(TestLockFreeQueue, MultiThreadedTest){
+
+ // Push and pop from the queue concurrently
+ // Check that the queue is empty
+ // Check that the order of the read values matches the order of the written values
+
+ constexpr int kQueueCapacity = 2048;
+ LockFreeQueue<int, kQueueCapacity> myQ;
+ std::array<int, kQueueCapacity> sourceData;
+ std::array<int, kQueueCapacity> targetData { 0 };
+
+ // Populate the source data
+ for (int i = 0; i < kQueueCapacity; i++){
+ sourceData[i] = i;
+ }
+
+ std::thread writer([&myQ, &sourceData](){
+ for (int i = 0; i < kQueueCapacity; i++){
+ myQ.push(sourceData[i]);
+ }
+ });
+
+ std::thread reader([&myQ, &targetData](){
+ for (int i = 0; i < kQueueCapacity; i++){
+ // pop the latest element, or wait for it to become available
+ while (!myQ.pop(targetData[i])){
+ std::cout << "Waiting for element at index " << i << " to be written" << std::endl;
+ usleep(500);
+ }
+ }
+ });
+
+ writer.join();
+ reader.join();
+
+ int v;
+ ASSERT_FALSE(myQ.pop(v));
+
+ // Check that the target matches the source
+ for (int i = 0; i < kQueueCapacity; i++){
+ ASSERT_EQ(sourceData[i], targetData[i]) << "Elements at index " << i << " did not match";
+ }
+}