xref: /aosp_15_r20/external/oboe/samples/RhythmGame/test/testLockFreeQueue.cpp (revision 05767d913155b055644481607e6fa1e35e2fe72c)
1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <thread>
18 #include <iostream>
19 
20 #include <gtest/gtest.h>
21 #include "utils/LockFreeQueue.h"
22 
23 /**
24  * Tests
25  * =====
26  *
27  * READ/POP:
28  *  - if queue is empty return false
29  *  - if queue has elements return true
30  *  - returns correct value once
31  *  - returns correct value more than once
32  *
33  * WRITE/PUSH:
34  *  - if queue is full return false
35  *  - if queue has space return true
36  *
37  * WRAP:
38  *  - read from full queue, then write one more element, then correct read
39  *
40  * PEEK:
41  *  - peek once
42  *  - peek twice
43  *
44  * SIZE:
45  *  - correct zero
46  *  - correct once
47  *  - correct many
48  *
49  * MULTI-THREADED:
50  *  - write and read concurrently, queue is empty at end and item order is maintained
51  *
52  * CANNOT TEST:
53  *  - read/writes correct after wraparound (takes too long to run)
54  *  - Constructing queue with non power of 2 capacity results in assert failure (needs death test)
55  *  - Constructing queue with signed type results in assert failure (needs death test)
56  *
57  */
58 
59 constexpr int kQueueCapacity = 2;
60 
61 class TestLockFreeQueue : public ::testing::Test {
62 
63 public:
64 
65     int result1;
66     int result2;
67     int result3;
68     LockFreeQueue<int, kQueueCapacity> q;
69 };
70 
TEST_F(TestLockFreeQueue,PopReturnsFalseWhenEmpty)71 TEST_F(TestLockFreeQueue, PopReturnsFalseWhenEmpty){
72     bool result = q.pop(result1);
73     ASSERT_EQ(result, false);
74 }
75 
TEST_F(TestLockFreeQueue,PopReturnsTrueWhenNotEmpty)76 TEST_F(TestLockFreeQueue, PopReturnsTrueWhenNotEmpty){
77     q.push(123);
78     bool result = q.pop(result1);
79     ASSERT_EQ(result, true);
80 }
81 
TEST_F(TestLockFreeQueue,PopReturnsOneCorrectValue)82 TEST_F(TestLockFreeQueue, PopReturnsOneCorrectValue){
83     q.push(123);
84     q.pop(result1);
85     ASSERT_EQ(result1, 123);
86 }
87 
TEST_F(TestLockFreeQueue,PopReturnsManyCorrectValues)88 TEST_F(TestLockFreeQueue, PopReturnsManyCorrectValues){
89     q.push(123);
90     q.push(456);
91     q.pop(result1);
92     q.pop(result2);
93     ASSERT_EQ(result1, 123);
94     ASSERT_EQ(result2, 456);
95 }
96 
TEST_F(TestLockFreeQueue,PushWhenFullReturnsFalse)97 TEST_F(TestLockFreeQueue, PushWhenFullReturnsFalse){
98     q.push(123);
99     q.push(123);
100     ASSERT_EQ(q.push(123), false);
101 }
102 
TEST_F(TestLockFreeQueue,PushWhenSpaceAvailableReturnsTrue)103 TEST_F(TestLockFreeQueue, PushWhenSpaceAvailableReturnsTrue){
104     ASSERT_EQ(q.push(123), true);
105 }
106 
TEST_F(TestLockFreeQueue,PushHandlesWrapCorrectly)107 TEST_F(TestLockFreeQueue, PushHandlesWrapCorrectly){
108     q.push(123);
109     q.push(234);
110     q.pop(result1);
111     q.push(999);
112     q.pop(result2);
113     ASSERT_EQ(result2, 234);
114     q.pop(result3);
115     ASSERT_EQ(result3, 999);
116 }
117 
TEST_F(TestLockFreeQueue,PeekWhenEmptyReturnsFalse)118 TEST_F(TestLockFreeQueue, PeekWhenEmptyReturnsFalse){
119     ASSERT_EQ(q.peek(result1), false);
120 }
121 
TEST_F(TestLockFreeQueue,PeekWhenNotEmptyReturnsTrue)122 TEST_F(TestLockFreeQueue, PeekWhenNotEmptyReturnsTrue){
123     q.push(123);
124     ASSERT_EQ(q.peek(result1), true);
125 }
126 
TEST_F(TestLockFreeQueue,PeekReturnsCorrectValueOnce)127 TEST_F(TestLockFreeQueue, PeekReturnsCorrectValueOnce){
128     q.push(456);
129     q.peek(result1);
130     ASSERT_EQ(result1, 456);
131 }
132 
TEST_F(TestLockFreeQueue,PeekReturnsCorrectValueTwice)133 TEST_F(TestLockFreeQueue, PeekReturnsCorrectValueTwice){
134     q.push(456);
135     q.peek(result1);
136     q.peek(result2);
137     ASSERT_EQ(result2, 456);
138 }
139 
TEST_F(TestLockFreeQueue,SizeReturnsZeroAfterInit)140 TEST_F(TestLockFreeQueue, SizeReturnsZeroAfterInit){
141     ASSERT_EQ(q.size(), 0);
142 }
143 
TEST_F(TestLockFreeQueue,SizeIsOneAfterPushOnce)144 TEST_F(TestLockFreeQueue, SizeIsOneAfterPushOnce){
145     q.push(321);
146     ASSERT_EQ(q.size(), 1);
147 }
148 
TEST_F(TestLockFreeQueue,SizeIsCorrectAfterPushingMaxItems)149 TEST_F(TestLockFreeQueue, SizeIsCorrectAfterPushingMaxItems){
150     for (int i = 0; i < kQueueCapacity; ++i) { q.push(i); }
151     ASSERT_EQ(q.size(), kQueueCapacity);
152 }
153 
TEST_F(TestLockFreeQueue,SizeCorrectAfterWriteCounterWraps)154 TEST_F(TestLockFreeQueue, SizeCorrectAfterWriteCounterWraps){
155 
156     const uint32_t kCapacity = (uint32_t) 1 << 7;
157     LockFreeQueue<int, kCapacity, uint8_t> myQ;
158 
159     for (int i = 0; i < UINT8_MAX; ++i) {
160         myQ.push(i);
161         myQ.pop(result1);
162     }
163 
164     myQ.push(1);
165     ASSERT_EQ(myQ.size(), 1);
166 }
167 
TEST_F(TestLockFreeQueue,MultiThreadedTest)168 TEST_F(TestLockFreeQueue, MultiThreadedTest){
169 
170     // Push and pop from the queue concurrently
171     // Check that the queue is empty
172     // Check that the order of the read values matches the order of the written values
173 
174     constexpr int kQueueCapacity = 2048;
175     LockFreeQueue<int, kQueueCapacity> myQ;
176     std::array<int, kQueueCapacity> sourceData;
177     std::array<int, kQueueCapacity> targetData { 0 };
178 
179     // Populate the source data
180     for (int i = 0; i < kQueueCapacity; i++){
181         sourceData[i] = i;
182     }
183 
184     std::thread writer([&myQ, &sourceData](){
185         for (int i = 0; i < kQueueCapacity; i++){
186             myQ.push(sourceData[i]);
187         }
188     });
189 
190     std::thread reader([&myQ, &targetData](){
191         for (int i = 0; i < kQueueCapacity; i++){
192             // pop the latest element, or wait for it to become available
193             while (!myQ.pop(targetData[i])){
194                 std::cout << "Waiting for element at index " << i << " to be written" << std::endl;
195                 usleep(500);
196             }
197         }
198     });
199 
200     writer.join();
201     reader.join();
202 
203     int v;
204     ASSERT_FALSE(myQ.pop(v));
205 
206     // Check that the target matches the source
207     for (int i = 0; i < kQueueCapacity; i++){
208         ASSERT_EQ(sourceData[i], targetData[i]) << "Elements at index " << i << " did not match";
209     }
210 }
211