1 /*
2  * Copyright (C) 2019 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 "../BlockingQueue.h"
18 
19 
20 #include <gtest/gtest.h>
21 #include <thread>
22 
23 namespace android {
24 
25 
26 // --- BlockingQueueTest ---
27 
28 /**
29  * Sanity check of basic pop and push operation.
30  */
TEST(BlockingQueueTest,Queue_AddAndRemove)31 TEST(BlockingQueueTest, Queue_AddAndRemove) {
32     constexpr size_t capacity = 10;
33     BlockingQueue<int> queue(capacity);
34 
35     ASSERT_TRUE(queue.push(1));
36     ASSERT_EQ(queue.pop(), 1);
37 }
38 
39 /**
40  * Make sure the queue has strict capacity limits.
41  */
TEST(BlockingQueueTest,Queue_ReachesCapacity)42 TEST(BlockingQueueTest, Queue_ReachesCapacity) {
43     constexpr size_t capacity = 3;
44     BlockingQueue<int> queue(capacity);
45 
46     // First 3 elements should be added successfully
47     ASSERT_TRUE(queue.push(1));
48     ASSERT_TRUE(queue.push(2));
49     ASSERT_TRUE(queue.push(3));
50     ASSERT_FALSE(queue.push(4)) << "Queue should reach capacity at size " << capacity;
51 }
52 
53 /**
54  * Make sure the queue maintains FIFO order.
55  * Add elements and remove them, and check the order.
56  */
TEST(BlockingQueueTest,Queue_isFIFO)57 TEST(BlockingQueueTest, Queue_isFIFO) {
58     constexpr size_t capacity = 10;
59     BlockingQueue<int> queue(capacity);
60 
61     for (size_t i = 0; i < capacity; i++) {
62         ASSERT_TRUE(queue.push(static_cast<int>(i)));
63     }
64     for (size_t i = 0; i < capacity; i++) {
65         ASSERT_EQ(queue.pop(), static_cast<int>(i));
66     }
67 }
68 
TEST(BlockingQueueTest,Queue_Clears)69 TEST(BlockingQueueTest, Queue_Clears) {
70     constexpr size_t capacity = 2;
71     BlockingQueue<int> queue(capacity);
72 
73     queue.push(1);
74     queue.push(2);
75     queue.clear();
76     queue.push(3);
77     // Should no longer receive elements 1 and 2
78     ASSERT_EQ(3, queue.pop());
79 }
80 
TEST(BlockingQueueTest,Queue_Erases)81 TEST(BlockingQueueTest, Queue_Erases) {
82     constexpr size_t capacity = 4;
83     BlockingQueue<int> queue(capacity);
84 
85     queue.push(1);
86     queue.push(2);
87     queue.push(3);
88     queue.push(4);
89     // Erase elements 2 and 4
90     queue.erase([](int element) { return element == 2 || element == 4; });
91     // Should no longer receive elements 2 and 4
92     ASSERT_EQ(1, queue.pop());
93     ASSERT_EQ(3, queue.pop());
94 }
95 
96 // --- BlockingQueueTest - Multiple threads ---
97 
TEST(BlockingQueueTest,Queue_AllowsMultipleThreads)98 TEST(BlockingQueueTest, Queue_AllowsMultipleThreads) {
99     constexpr size_t capacity = 100; // large capacity to increase likelihood that threads overlap
100     BlockingQueue<int> queue(capacity);
101 
102     // Fill queue from a different thread
103     std::thread fillQueue([&queue](){
104         for (size_t i = 0; i < capacity; i++) {
105             ASSERT_TRUE(queue.push(static_cast<int>(i)));
106         }
107     });
108 
109     // Make sure all elements are received in correct order
110     for (size_t i = 0; i < capacity; i++) {
111         ASSERT_EQ(queue.pop(), static_cast<int>(i));
112     }
113 
114     fillQueue.join();
115 }
116 
117 /**
118  * When the queue has no elements, and pop is called, it should block
119  * the current thread until an element is added to the queue (from another thread).
120  * Here we create a separate thread and call pop on an empty queue. Next,
121  * we check that the thread is blocked.
122  */
TEST(BlockingQueueTest,Queue_BlocksWhileWaitingForElements)123 TEST(BlockingQueueTest, Queue_BlocksWhileWaitingForElements) {
124     constexpr size_t capacity = 1;
125     BlockingQueue<int> queue(capacity);
126 
127     std::atomic_bool hasReceivedElement = false;
128 
129     // fill queue from a different thread
130     std::thread waitUntilHasElements([&queue, &hasReceivedElement](){
131         queue.pop(); // This should block until an element has been added
132         hasReceivedElement = true;
133     });
134 
135     ASSERT_FALSE(hasReceivedElement);
136     queue.push(1);
137     waitUntilHasElements.join();
138     ASSERT_TRUE(hasReceivedElement);
139 }
140 
141 
142 } // namespace android
143