• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 #include <thread>
17 #include <unistd.h>
18 
19 #include <gtest/gtest.h>
20 
21 #include "common/vsoc/lib/circqueue_impl.h"
22 #include "common/vsoc/lib/mock_region_view.h"
23 
24 #define EXPECT_BLOCK(region, tid) \
25     EXPECT_TRUE(region.IsBlocking(tid))
26 
27 namespace {
28 
29 constexpr int kQueueSizeLog2 = 16;
30 constexpr int kQueueCapacity = 1 << kQueueSizeLog2;
31 constexpr int kMaxPacketSize = 1024;
32 
33 constexpr int kNumReadingThread = 5;
34 constexpr int kNumWritingThread = 5;
35 
36 struct CircQueueTestRegionLayout : public vsoc::layout::RegionLayout {
37   vsoc::layout::CircularByteQueue<kQueueSizeLog2> byte_queue;
38   vsoc::layout::CircularPacketQueue<kQueueSizeLog2, kMaxPacketSize> packet_queue;
39 };
40 
41 typedef vsoc::test::MockRegionView<CircQueueTestRegionLayout> CircQueueRegionView;
42 
43 class CircQueueTest : public ::testing::Test {
44  protected:
SetUp()45   virtual void SetUp() {
46     region_.Open();
47   }
48 
49   CircQueueRegionView region_;
50 };
51 
ReadBytes(CircQueueRegionView * region,int bytes)52 intptr_t ReadBytes(CircQueueRegionView* region, int bytes) {
53   char buffer_out[bytes];
54   CircQueueTestRegionLayout* layout = region->data();
55   return layout->byte_queue.Read(region, buffer_out, bytes);
56 }
57 
WriteBytes(CircQueueRegionView * region,int bytes)58 intptr_t WriteBytes(CircQueueRegionView* region, int bytes) {
59   char buffer_in[bytes];
60   CircQueueTestRegionLayout* layout = region->data();
61   return layout->byte_queue.Write(region, buffer_in, bytes);
62 }
63 
ReadPacket(CircQueueRegionView * region,int max_size)64 intptr_t ReadPacket(CircQueueRegionView* region, int max_size) {
65   char buffer_out[max_size];
66   CircQueueTestRegionLayout* layout = region->data();
67   return layout->packet_queue.Read(region, buffer_out, max_size);
68 }
69 
WritePacket(CircQueueRegionView * region,int packet_size)70 intptr_t WritePacket(CircQueueRegionView* region, int packet_size) {
71   char buffer_in[packet_size];
72   CircQueueTestRegionLayout* layout = region->data();
73   return layout->packet_queue.Write(region, buffer_in, packet_size);
74 }
75 
ReadBytesInChunk(CircQueueRegionView * region,int total_size,int chuck_size)76 void ReadBytesInChunk(CircQueueRegionView* region, int total_size, int chuck_size) {
77   char buffer_out[chuck_size];
78   CircQueueTestRegionLayout* layout = region->data();
79   int total_read = 0;
80   int remaining = total_size;
81   while (remaining >= chuck_size) {
82     int ret = layout->byte_queue.Read(region, buffer_out, chuck_size);
83     total_read += ret;
84     remaining -= ret;
85   }
86   if (remaining > 0) {
87     total_read += layout->byte_queue.Write(region, buffer_out, remaining);
88   }
89   EXPECT_EQ(total_read, total_size);
90 }
91 
WriteBytesInChunk(CircQueueRegionView * region,int total_size,int chuck_size)92 void WriteBytesInChunk(CircQueueRegionView* region, int total_size, int chuck_size) {
93   char buffer_in[chuck_size];
94   CircQueueTestRegionLayout* layout = region->data();
95   int total_write = 0;
96   int remaining = total_size;
97   while (remaining >= chuck_size) {
98     int ret = layout->byte_queue.Write(region, buffer_in, chuck_size);
99     total_write += ret;
100     remaining -= ret;
101   }
102   if (remaining > 0) {
103     total_write += layout->byte_queue.Write(region, buffer_in, remaining);
104   }
105   EXPECT_EQ(total_write, total_size);
106 }
107 
ReadManyPackets(CircQueueRegionView * region,int num_packets,int packet_size)108 void ReadManyPackets(CircQueueRegionView* region, int num_packets, int packet_size) {
109   char buffer_out[packet_size];
110   CircQueueTestRegionLayout* layout = region->data();
111   int total_read = 0;
112   int remaining = num_packets;
113   while (remaining > 0) {
114     int ret = layout->packet_queue.Read(region, buffer_out, packet_size);
115     total_read += ret;
116     remaining--;
117   }
118   EXPECT_EQ(total_read, num_packets * packet_size);
119 }
120 
WriteManyPackets(CircQueueRegionView * region,int num_packets,int packet_size)121 void WriteManyPackets(CircQueueRegionView* region, int num_packets, int packet_size) {
122   char buffer_in[packet_size];
123   CircQueueTestRegionLayout* layout = region->data();
124   int total_write = 0;
125   int remaining = num_packets;
126   while (remaining > 0) {
127     int ret = layout->packet_queue.Write(region, buffer_in, packet_size);
128     total_write += ret;
129     remaining--;
130   }
131   EXPECT_EQ(total_write, num_packets * packet_size);
132 }
133 
134 // ByteQueue Tests
135 
136 // Test writing bytes
TEST_F(CircQueueTest,ByteQueueSimpleWrite)137 TEST_F(CircQueueTest, ByteQueueSimpleWrite) {
138   const int num_bytes = 8;
139   EXPECT_EQ(num_bytes, WriteBytes(&this->region_, num_bytes));
140 }
141 
142 // Test reading bytes
TEST_F(CircQueueTest,ByteQueueSimpleRead)143 TEST_F(CircQueueTest, ByteQueueSimpleRead) {
144   const int num_bytes = 8;
145   EXPECT_EQ(num_bytes, WriteBytes(&this->region_, num_bytes));
146   EXPECT_EQ(num_bytes, ReadBytes(&this->region_, num_bytes));
147 }
148 
149 // Test reading on an empty queue. Expect blocking.
TEST_F(CircQueueTest,ByteQueueReadOnEmpty)150 TEST_F(CircQueueTest, ByteQueueReadOnEmpty) {
151   const int num_bytes = 8;
152 
153   // Spawn a thread to read from queue. Expect it to block.
154   std::thread reading_thread(ReadBytes, &this->region_, num_bytes);
155   EXPECT_BLOCK(region_, reading_thread.get_id());
156 
157   // Write expected bytes in so that we can clean up properly.
158   std::thread writing_thread(WriteBytes, &this->region_, num_bytes);
159   writing_thread.join();
160 
161   reading_thread.join();
162 }
163 
164 // Test writing on a full queue. Expect blocking.
TEST_F(CircQueueTest,ByteQueueWriteOnFull)165 TEST_F(CircQueueTest, ByteQueueWriteOnFull) {
166   // Fill the queue.
167   const int capacity_bytes = kQueueCapacity;
168   EXPECT_EQ(capacity_bytes, WriteBytes(&this->region_, capacity_bytes));
169 
170   // Now the queue is full, any further write would block.
171   const int num_bytes = 8;
172   std::thread writing_thread(WriteBytes, &this->region_, num_bytes);
173   EXPECT_BLOCK(region_, writing_thread.get_id());
174 
175   // Read the extra bytes out so that we can clean up properly.
176   std::thread reading_thread(ReadBytes, &this->region_, num_bytes);
177   reading_thread.join();
178 
179   writing_thread.join();
180 }
181 
182 // Test if bytes being read out are the same as ones being written in.
TEST_F(CircQueueTest,ByteQueueContentIntegrity)183 TEST_F(CircQueueTest, ByteQueueContentIntegrity) {
184   const int num_bytes = 8;
185   CircQueueTestRegionLayout* layout = this->region_.data();
186 
187   char buffer_in[num_bytes] = {'a'};
188   layout->byte_queue.Write(&this->region_, buffer_in, num_bytes);
189 
190   char buffer_out[num_bytes] = {'b'};
191   layout->byte_queue.Read(&this->region_, buffer_out, num_bytes);
192 
193   for (int i=0; i<num_bytes; i++) {
194     EXPECT_EQ(buffer_in[i], buffer_out[i]);
195   }
196 }
197 
198 // Test writing more bytes than capacity
TEST_F(CircQueueTest,ByteQueueWriteTooManyBytes)199 TEST_F(CircQueueTest, ByteQueueWriteTooManyBytes) {
200   const int extra_bytes = 8;
201   const int num_bytes = kQueueCapacity + extra_bytes;
202   EXPECT_EQ(-ENOSPC, WriteBytes(&this->region_, num_bytes));
203 }
204 
205 // Test multiple bytes read/write
TEST_F(CircQueueTest,ByteQueueMultipleReadWrite)206 TEST_F(CircQueueTest, ByteQueueMultipleReadWrite) {
207   const int chunk_size = 7;
208   const int total_size = 3.3 * kQueueCapacity;
209   std::vector<std::thread> reading_threads;
210   std::vector<std::thread> writing_threads;
211   for (int i=0; i<kNumReadingThread; i++) {
212     reading_threads.emplace_back(
213         std::thread(ReadBytesInChunk, &this->region_, total_size, chunk_size));
214   }
215   for (int i=0; i<kNumWritingThread; i++) {
216     writing_threads.emplace_back(
217         std::thread(WriteBytesInChunk, &this->region_, total_size, chunk_size));
218   }
219   std::for_each(reading_threads.begin(), reading_threads.end(), [](std::thread& t) { t.join(); });
220   std::for_each(writing_threads.begin(), writing_threads.end(), [](std::thread& t) { t.join(); });
221 }
222 
223 // PacketQueue Tests
224 
225 // Test writing packet
TEST_F(CircQueueTest,PacketQueueSimpleWrite)226 TEST_F(CircQueueTest, PacketQueueSimpleWrite) {
227   const int packet_size = 8;
228   EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
229 }
230 
231 // Test reading packet
TEST_F(CircQueueTest,PacketQueueSimpleRead)232 TEST_F(CircQueueTest, PacketQueueSimpleRead) {
233   const int packet_size = 8;
234   EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
235   EXPECT_EQ(packet_size, ReadPacket(&this->region_, packet_size));
236 }
237 
238 // Test reading on an empty queue. Expect blocking.
TEST_F(CircQueueTest,PacketQueueReadOnEmpty)239 TEST_F(CircQueueTest, PacketQueueReadOnEmpty) {
240   const int packet_size = 8;
241 
242   // Spawn a thread to read from queue. Expect it to block.
243   std::thread reading_thread(ReadPacket, &this->region_, packet_size);
244   EXPECT_BLOCK(region_, reading_thread.get_id());
245 
246   // Write expected bytes in so that we can clean up properly.
247   std::thread writing_thread(WritePacket, &this->region_, packet_size);
248   writing_thread.join();
249 
250   reading_thread.join();
251 }
252 
253 // Test writing on a full queue. Expect blocking.
TEST_F(CircQueueTest,PacketQueueWriteOnFull)254 TEST_F(CircQueueTest, PacketQueueWriteOnFull) {
255   // Fill the queue.
256   const int packet_size = kMaxPacketSize;
257   int capacity_bytes = kQueueCapacity;
258   while (capacity_bytes >= packet_size) {
259     EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
260     capacity_bytes -= (packet_size + sizeof(uint32_t));
261   }
262 
263   // Now the queue is full, any further write would block.
264   std::thread writing_thread(WritePacket, &this->region_, packet_size);
265   EXPECT_BLOCK(region_, writing_thread.get_id());
266 
267   // Read the extra bytes out so that we can clean up properly.
268   std::thread reading_thread(ReadPacket, &this->region_, packet_size);
269   reading_thread.join();
270 
271   writing_thread.join();
272 }
273 
274 // Test if packet being read out are the same as one being written in.
TEST_F(CircQueueTest,PacketQueueContentIntegrity)275 TEST_F(CircQueueTest, PacketQueueContentIntegrity) {
276   const int packet_size = 8;
277   CircQueueTestRegionLayout* layout = this->region_.data();
278 
279   char buffer_in[packet_size] = {'a'};
280   layout->packet_queue.Write(&this->region_, buffer_in, packet_size);
281 
282   char buffer_out[packet_size] = {'b'};
283   layout->packet_queue.Read(&this->region_, buffer_out, packet_size);
284 
285   for (int i=0; i<packet_size; i++) {
286     EXPECT_EQ(buffer_in[i], buffer_out[i]);
287   }
288 }
289 
290 // Test writing packet larger than capacity
TEST_F(CircQueueTest,PacketQueueWriteTooLargePacket)291 TEST_F(CircQueueTest, PacketQueueWriteTooLargePacket) {
292   const int extra_bytes = 8;
293   const int packet_size = kQueueCapacity + extra_bytes;
294   EXPECT_EQ(-ENOSPC, WritePacket(&this->region_, packet_size));
295 }
296 
297 // Test reading packet larger than can handle
TEST_F(CircQueueTest,PacketQueueReadTooLargePacket)298 TEST_F(CircQueueTest, PacketQueueReadTooLargePacket) {
299   const int extra_bytes = 8;
300   const int small_packet = 8;
301   const int large_packet = small_packet + extra_bytes;
302 
303   WritePacket(&this->region_, large_packet);
304   char buffer_out[small_packet];
305   CircQueueTestRegionLayout* layout = this->region_.data();
306   EXPECT_EQ(-ENOSPC, layout->packet_queue.Read(&this->region_, buffer_out, small_packet));
307 }
308 
309 // Test multiple packets read/write
TEST_F(CircQueueTest,PacketQueueMultipleReadWrite)310 TEST_F(CircQueueTest, PacketQueueMultipleReadWrite) {
311   const int packet_size = kMaxPacketSize;
312   const int num_packets = 1.5 * (kQueueCapacity / packet_size);
313   std::vector<std::thread> reading_threads;
314   std::vector<std::thread> writing_threads;
315   for (int i=0; i<kNumReadingThread; i++) {
316     reading_threads.emplace_back(
317         std::thread(ReadManyPackets, &this->region_, num_packets, packet_size));
318   }
319   for (int i=0; i<kNumWritingThread; i++) {
320     writing_threads.emplace_back(
321         std::thread(WriteManyPackets, &this->region_, num_packets, packet_size));
322   }
323   std::for_each(reading_threads.begin(), reading_threads.end(), [](std::thread& t) { t.join(); });
324   std::for_each(writing_threads.begin(), writing_threads.end(), [](std::thread& t) { t.join(); });
325 }
326 
327 }  // namespace
328