OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include <algorithm> | |
12 #include <map> | |
13 #include <tuple> | |
14 #include <vector> | |
15 | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 #include "webrtc/common_audio/swap_queue.h" | |
18 #include "webrtc/modules/interface/module_common_types.h" | |
19 #include "webrtc/system_wrappers/interface/event_wrapper.h" | |
20 #include "webrtc/system_wrappers/interface/sleep.h" | |
21 #include "webrtc/system_wrappers/interface/thread_wrapper.h" | |
22 | |
23 namespace webrtc { | |
24 | |
25 namespace { | |
26 | |
27 // Provides thread_safe random numbers. | |
28 class ThreadSafeRandomNumberGenerator { | |
kwiberg-webrtc
2015/10/13 13:54:24
It's not thread safe, it just avoids being terribl
peah-webrtc
2015/10/14 12:16:48
Fully true, I rewrote it using a functor instead.
| |
29 public: | |
30 ThreadSafeRandomNumberGenerator() : ThreadSafeRandomNumberGenerator(10000) {} | |
31 explicit ThreadSafeRandomNumberGenerator(int size) { | |
32 srand(42); | |
33 random_numbers_.resize(size); | |
34 for (int k = 0; k < size; k++) { | |
35 random_numbers_[k] = rand(); | |
the sun
2015/10/14 13:05:21
Note that it doesn't matter that we make rand() th
peah-webrtc
2015/10/15 07:34:59
You are fully correct! If ok with you, I'll keep t
the sun
2015/10/15 08:44:00
Not ok. It still has global mutable state, implici
peah-webrtc
2015/10/26 08:56:57
I definitely agree on the issues with non-determin
| |
36 } | |
37 } | |
38 | |
39 // Initializes a user of the random number generator and | |
40 // returns a user id to use when aquiring a random number. | |
41 int AddUser(int seed) { | |
42 int next_user_index = GetNextIserId(); | |
43 | |
44 user_indices_[next_user_index] = seed % random_numbers_.size(); | |
45 return next_user_index; | |
46 } | |
47 | |
48 // Returns a random number between 0 and RAND_MAX. | |
49 int Rand(int user_id) { | |
50 int index = user_indices_[user_id]; | |
51 index = (index < (static_cast<int>(random_numbers_.size()) - 1) ? index + 1 | |
52 : 0); | |
53 user_indices_[user_id] = index; | |
54 return random_numbers_[index]; | |
55 } | |
kwiberg-webrtc
2015/10/13 13:54:24
Why do you need to support several isers? Couldn't
peah-webrtc
2015/10/14 12:16:48
Fully true, I rewrote it using a functor instead.
| |
56 | |
57 private: | |
58 int GetNextIserId() { | |
59 int max_user_id = -1; | |
60 for (auto it = user_indices_.begin(); it != user_indices_.end(); ++it) { | |
61 max_user_id = std::max(max_user_id, it->first); | |
62 } | |
63 | |
64 return max_user_id + 1; | |
65 } | |
66 | |
67 std::vector<int> random_numbers_; | |
68 std::map<int, int> user_indices_; | |
69 }; | |
kwiberg-webrtc
2015/10/13 13:54:24
Wouldn't it be simpler to just protect rand() call
peah-webrtc
2015/10/14 12:16:48
Fully true! This is definitely over-engineered! I
the sun
2015/10/15 08:44:00
See above comment. You will still have non-determi
peah-webrtc
2015/10/26 08:56:57
Acknowledged.
| |
70 | |
71 // The type for a message. | |
72 class SwapQueueTestMessage { | |
73 public: | |
74 SwapQueueTestMessage() { | |
75 id_ = -1; | |
76 info_ = -1; | |
77 } | |
78 | |
79 SwapQueueTestMessage(const SwapQueueTestMessage& m) | |
80 : id_(m.id_), info_(m.info_) {} | |
81 | |
82 SwapQueueTestMessage(int id, int info) { | |
83 id_ = id; | |
84 info_ = info; | |
85 } | |
86 | |
87 bool Compare(const SwapQueueTestMessage& m) { | |
88 return ((id_ == m.id_) && (info_ == m.info_)); | |
89 } | |
90 | |
91 void swap(SwapQueueTestMessage& m) { | |
92 std::swap(id_, m.id_); | |
93 std::swap(info_, m.info_); | |
94 } | |
95 | |
96 private: | |
97 int id_; | |
98 int info_; | |
99 }; | |
100 | |
101 // Implements the tests for the sample queue. | |
102 class SwapQueueMessageTest | |
103 : public ::testing::TestWithParam<::testing::tuple<size_t, size_t>> { | |
104 public: | |
105 SwapQueueMessageTest() | |
106 : test_complete_(EventWrapper::Create()), | |
107 reader_thread_( | |
108 ThreadWrapper::CreateThread(CbReaderThread, this, "reader")), | |
109 writer_thread_( | |
110 ThreadWrapper::CreateThread(CbWriterThread, this, "writer")) {} | |
111 | |
112 // Run the test with a timeout. | |
113 EventTypeWrapper RunTest() { | |
114 StartThreads(); | |
115 return test_complete_->Wait(kTestTimeOutLimit); | |
116 } | |
117 | |
118 virtual void SetUp() { | |
119 // Read test parameter | |
120 size_t queue_size = static_cast<size_t>(testing::get<0>(GetParam())); | |
121 size_t messages_size = static_cast<size_t>(testing::get<1>(GetParam())); | |
122 | |
123 // Create queues. | |
124 queue_.reset(new SwapQueue<SwapQueueTestMessage>(queue_size)); | |
125 | |
126 // Create and populate message vector. | |
127 messages_ = std::vector<SwapQueueTestMessage>(); | |
128 CreateTestData(messages_size); | |
129 | |
130 // Setup the random number generator. | |
131 reader_rand_id_ = random_generator_.AddUser(42); | |
132 writer_rand_id_ = random_generator_.AddUser(37); | |
133 } | |
134 | |
135 virtual void TearDown() { | |
136 reader_thread_->Stop(); | |
137 writer_thread_->Stop(); | |
138 } | |
139 | |
140 private: | |
141 const int kTestTimeOutLimit = 10 * 1000; | |
142 | |
143 // Populates the data vectors with random values. | |
144 void CreateTestData(int messages_size) { | |
145 messages_.resize(messages_size); | |
146 for (size_t k = 0; k < messages_.size(); k++) | |
147 messages_[k] = | |
148 SwapQueueTestMessage(random_generator_.Rand(writer_rand_id_) % 10000, | |
149 random_generator_.Rand(writer_rand_id_) % 10000); | |
150 } | |
151 | |
152 // Thread callback for the reader thread. | |
153 static bool CbReaderThread(void* context) { | |
154 return reinterpret_cast<SwapQueueMessageTest*>(context)->CbReaderImpl(); | |
155 } | |
156 | |
157 // Thread callback for the writer thread. | |
158 static bool CbWriterThread(void* context) { | |
159 return reinterpret_cast<SwapQueueMessageTest*>(context)->CbWriterImpl(); | |
160 } | |
161 | |
162 // Tests in a threadsafe manner whether all the samples have been | |
163 // written and read. | |
164 bool TestDone() { return (GetNumMessagesRead() == messages_.size()); } | |
165 | |
166 // Returns in a threadsafe manner the number of frames read from the queue. | |
167 size_t GetNumMessagesRead() const { | |
168 rtc::CritScope cs(&crit_); | |
169 return num_messages_read_; | |
170 } | |
171 | |
172 // Increases in a threadsafe manner the number of messages | |
173 // read from the queue. | |
174 void IncreaseMessageReadCounter() { | |
175 rtc::CritScope cs(&crit_); | |
176 num_messages_read_++; | |
177 } | |
178 | |
179 // Implements the callback functionality for the reader thread. | |
180 bool CbReaderImpl() { | |
181 SleepRandomTime(3, reader_rand_id_); | |
182 | |
183 // Read the message and verify bitexactness. | |
184 SwapQueueTestMessage m; | |
185 if (queue_->Remove(&m)) { | |
186 EXPECT_TRUE(m.Compare(messages_[GetNumMessagesRead()])); | |
187 IncreaseMessageReadCounter(); | |
188 } | |
189 | |
190 // End the test if the test is done or an assert has occurred. | |
191 if (TestDone() || HasFatalFailure()) { | |
192 test_complete_->Set(); | |
193 } | |
194 | |
195 return true; | |
196 } | |
197 | |
198 // Implements the callback functionality for the writer thread. | |
199 bool CbWriterImpl() { | |
200 SleepRandomTime(3, writer_rand_id_); | |
201 | |
202 if (num_messages_written_ < messages_.size()) { | |
203 // Attempt to put the message in the queue. | |
204 SwapQueueTestMessage m(messages_[num_messages_written_]); | |
205 if (queue_->Insert(&m)) | |
206 num_messages_written_++; | |
207 } | |
208 | |
209 // End the test early if a fatal failure (ASSERT_*) has occurred. | |
210 if (HasFatalFailure()) | |
211 test_complete_->Set(); | |
212 | |
213 return true; | |
214 } | |
215 | |
216 // Sleeps a random time. | |
217 void SleepRandomTime(int max_sleep, int user_id) { | |
218 SleepMs(random_generator_.Rand(user_id) % (max_sleep + 1)); | |
219 } | |
220 | |
221 // Start the threads used in the test. | |
222 void StartThreads() { | |
223 ASSERT_TRUE(reader_thread_->Start()); | |
224 reader_thread_->SetPriority(kRealtimePriority); | |
225 ASSERT_TRUE(writer_thread_->Start()); | |
226 writer_thread_->SetPriority(kRealtimePriority); | |
227 } | |
228 | |
229 // Test parameters. | |
230 rtc::scoped_ptr<SwapQueue<SwapQueueTestMessage>> queue_; | |
231 mutable rtc::CriticalSection crit_; | |
232 ThreadSafeRandomNumberGenerator random_generator_; | |
233 const rtc::scoped_ptr<EventWrapper> test_complete_; | |
234 rtc::scoped_ptr<ThreadWrapper> reader_thread_; | |
235 rtc::scoped_ptr<ThreadWrapper> writer_thread_; | |
236 int reader_rand_id_; | |
237 int writer_rand_id_; | |
238 std::vector<SwapQueueTestMessage> messages_; | |
239 size_t num_messages_written_ = 0; | |
240 size_t num_messages_read_ GUARDED_BY(crit_) = 0; | |
241 }; | |
242 | |
243 // Implements the tests sample queue. | |
244 class SwapQueueSampleTest : public ::testing::TestWithParam< | |
245 ::testing::tuple<size_t, size_t, size_t>> { | |
246 public: | |
247 SwapQueueSampleTest() | |
248 : queue_size_(static_cast<size_t>(testing::get<0>(GetParam()))), | |
249 max_frame_length_(static_cast<size_t>(testing::get<1>(GetParam()))), | |
250 data_length_(static_cast<size_t>(testing::get<2>(GetParam()))), | |
251 test_complete_(EventWrapper::Create()), | |
252 reader_thread_( | |
253 ThreadWrapper::CreateThread(CbReaderThread, this, "reader")), | |
254 writer_thread_( | |
255 ThreadWrapper::CreateThread(CbWriterThread, this, "writer")) {} | |
256 | |
257 // Run the test with a timeout. | |
258 EventTypeWrapper RunTest() { | |
259 StartThreads(); | |
260 return test_complete_->Wait(kTestTimeOutLimit); | |
261 } | |
262 | |
263 virtual void SetUp() { | |
264 // Allocate read and write buffers. | |
265 buffer_reader_ = std::vector<int16_t>(max_frame_length_, 0); | |
266 buffer_writer_ = std::vector<int16_t>(max_frame_length_, 0); | |
267 | |
268 // Create queue. | |
269 std::vector<std::vector<int16_t>> template_queue(queue_size_); | |
270 for (size_t k = 0; k < queue_size_; k++) { | |
271 template_queue.resize(buffer_reader_.size()); | |
272 } | |
273 sample_queue_.reset(new SwapQueue<std::vector<int16_t>>(&template_queue)); | |
274 | |
275 // Create and populate data vectors. | |
276 CreateTestData(data_length_); | |
277 | |
278 // Setup the random number generator. | |
279 reader_rand_id_ = random_generator_.AddUser(42); | |
280 writer_rand_id_ = random_generator_.AddUser(37); | |
281 } | |
282 | |
283 virtual void TearDown() { | |
284 reader_thread_->Stop(); | |
285 writer_thread_->Stop(); | |
286 } | |
287 | |
288 private: | |
289 const int kTestTimeOutLimit = 10 * 1000; | |
290 | |
291 // Populates the data vectors with random values. | |
292 void CreateTestData(size_t data_length) { | |
293 samples_.resize(data_length); | |
294 for (size_t k = 0; k < data_length; k++) { | |
295 samples_[k] = | |
296 ((random_generator_.Rand(writer_rand_id_) % (32767 + 32768)) - 32768); | |
297 } | |
298 } | |
299 | |
300 // Thread callback for the reader thread. | |
301 static bool CbReaderThread(void* context) { | |
302 return reinterpret_cast<SwapQueueSampleTest*>(context)->CbReaderImpl(); | |
303 } | |
304 | |
305 // Thread callback for the writer thread. | |
306 static bool CbWriterThread(void* context) { | |
307 return reinterpret_cast<SwapQueueSampleTest*>(context)->CbWriterImpl(); | |
308 } | |
309 | |
310 // Tests in a threadsafe manner whether all the samples have been | |
311 // written and read. | |
312 bool TestDone() { return (GetNumSamplesRead() == data_length_); } | |
313 | |
314 // Returns in a threadsafe manner the number of frames read from the queue. | |
315 size_t GetNumFramesRead() { | |
316 rtc::CritScope cs(&crit_); | |
317 return num_frames_read_; | |
318 } | |
319 | |
320 // Returns in a threadsafe manner the number of samples read from the queue. | |
321 size_t GetNumSamplesRead() { | |
322 rtc::CritScope cs(&crit_); | |
323 return num_samples_read_; | |
324 } | |
325 | |
326 // Increases in a threadsafe manner the number of frames and samples | |
327 // read from the queue. | |
328 void IncreaseReadCounters(size_t num_samples_read) { | |
329 rtc::CritScope cs(&crit_); | |
330 num_frames_read_++; | |
331 num_samples_read_ += num_samples_read; | |
332 } | |
333 | |
334 // Implements the callback functionality for the reader thread. | |
335 bool CbReaderImpl() { | |
336 SleepRandomTime(3, reader_rand_id_); | |
337 | |
338 // Read the samples and verify bitexactness. | |
339 const size_t num_samples_read = GetNumSamplesRead(); | |
340 if (sample_queue_->Remove(&buffer_reader_)) { | |
341 for (size_t k = 0; k < buffer_reader_.size(); k++) | |
342 EXPECT_EQ(buffer_reader_[k], samples_[num_samples_read + k]); | |
343 | |
344 IncreaseReadCounters(buffer_reader_.size()); | |
345 } | |
346 | |
347 // End the test if the test is done or an assert has occurred. | |
348 if (TestDone() || HasFatalFailure()) { | |
349 test_complete_->Set(); | |
350 } | |
351 | |
352 return true; | |
353 } | |
354 | |
355 // Implements the callback functionality for the writer thread. | |
356 bool CbWriterImpl() { | |
357 SleepRandomTime(3, writer_rand_id_); | |
358 | |
359 // Choose number of samples to write. | |
360 const size_t num_samples_to_write = | |
361 std::min(random_generator_.Rand(writer_rand_id_) % max_frame_length_, | |
362 data_length_ - num_samples_written_); | |
363 | |
364 // Write the data. | |
365 bool data_written = false; | |
366 if (num_samples_to_write > 0) { | |
367 if (buffer_writer_.size() != num_samples_to_write) | |
368 buffer_writer_.resize(num_samples_to_write); | |
369 | |
370 memcpy(&buffer_writer_[0], &samples_[num_samples_written_], | |
371 num_samples_to_write * sizeof(samples_[0])); | |
372 | |
373 data_written = sample_queue_->Insert(&buffer_writer_); | |
374 } | |
375 | |
376 // Update the number of samples left to write | |
377 if (data_written) { | |
378 num_samples_written_ += num_samples_to_write; | |
379 num_frames_written_++; | |
380 } | |
381 | |
382 // End the test early if a fatal failure (ASSERT_*) has occurred. | |
383 if (HasFatalFailure()) | |
384 test_complete_->Set(); | |
385 | |
386 return true; | |
387 } | |
388 | |
389 // Sleeps a random time. | |
390 void SleepRandomTime(int max_sleep, int user_id) { | |
391 SleepMs(random_generator_.Rand(user_id) % (max_sleep + 1)); | |
392 } | |
393 | |
394 // Start the threads used in the test. | |
395 void StartThreads() { | |
396 ASSERT_TRUE(reader_thread_->Start()); | |
397 reader_thread_->SetPriority(kRealtimePriority); | |
398 ASSERT_TRUE(writer_thread_->Start()); | |
399 writer_thread_->SetPriority(kRealtimePriority); | |
400 } | |
401 | |
402 // Test parameters. | |
403 const size_t queue_size_; | |
404 const size_t max_frame_length_; | |
405 const size_t data_length_; | |
406 | |
407 rtc::scoped_ptr<SwapQueue<std::vector<int16_t>>> sample_queue_; | |
408 rtc::CriticalSection crit_; | |
409 ThreadSafeRandomNumberGenerator random_generator_; | |
410 const rtc::scoped_ptr<EventWrapper> test_complete_; | |
411 rtc::scoped_ptr<ThreadWrapper> reader_thread_; | |
412 rtc::scoped_ptr<ThreadWrapper> writer_thread_; | |
413 std::vector<int16_t> samples_; | |
414 size_t num_samples_read_ GUARDED_BY(crit_) = 0; | |
415 size_t num_samples_written_ = 0; | |
416 int reader_rand_id_; | |
417 int writer_rand_id_; | |
418 std::vector<int16_t> buffer_reader_; | |
419 std::vector<int16_t> buffer_writer_; | |
420 size_t num_frames_written_ = 0; | |
421 size_t num_frames_read_ GUARDED_BY(crit_) = 0; | |
422 }; | |
423 | |
424 // Test parameters for the message queue tests. | |
425 const size_t kMessageQueueSizes[] = {2, 7, 20}; | |
426 const size_t kMessageQueueDataLengths[] = {100}; | |
427 | |
428 // Test parameters for the sample queue tests. | |
429 const size_t kSampleQueueSizes[] = {7, 100}; | |
430 const size_t kMaxFrameLengths[] = {77, 160}; | |
431 const size_t kSampleQueueDataLengths[] = {200, 500}; | |
432 | |
433 } // anonymous namespace | |
434 | |
435 TEST(SwapQueueTest, FullQueue) { | |
436 SwapQueue<int> queue(2); | |
437 int i = 0; | |
438 EXPECT_TRUE(queue.Insert(&i)); | |
439 EXPECT_TRUE(queue.Insert(&i)); | |
440 EXPECT_FALSE(queue.Insert(&i)); | |
441 EXPECT_TRUE(queue.Remove(&i)); | |
442 EXPECT_TRUE(queue.Insert(&i)); | |
443 EXPECT_FALSE(queue.Insert(&i)); | |
444 } | |
445 | |
446 TEST(SwapQueueTest, EmptyQueue) { | |
447 SwapQueue<int> queue(2); | |
448 int i = 0; | |
449 EXPECT_FALSE(queue.Remove(&i)); | |
450 EXPECT_TRUE(queue.Insert(&i)); | |
451 EXPECT_TRUE(queue.Remove(&i)); | |
452 EXPECT_FALSE(queue.Remove(&i)); | |
453 } | |
454 | |
455 TEST(SwapQueueTest, InitializeTest) { | |
456 const size_t kQueueSize = 3; | |
457 std::vector<std::vector<int>> v(2); | |
458 v[0].resize(kQueueSize); | |
459 v[1].resize(kQueueSize); | |
460 SwapQueue<std::vector<int>> queue(&v); | |
461 std::vector<int> i(kQueueSize, 0); | |
462 | |
463 EXPECT_TRUE(queue.Insert(&i)); | |
464 EXPECT_EQ(i.size(), kQueueSize); | |
465 EXPECT_TRUE(queue.Insert(&i)); | |
466 EXPECT_EQ(i.size(), kQueueSize); | |
467 EXPECT_TRUE(queue.Remove(&i)); | |
468 EXPECT_EQ(i.size(), kQueueSize); | |
469 EXPECT_TRUE(queue.Remove(&i)); | |
470 EXPECT_EQ(i.size(), kQueueSize); | |
471 } | |
472 | |
473 TEST_P(SwapQueueSampleTest, BitExactness) { | |
474 // Run test and verify that it did not time out. | |
475 EXPECT_EQ(kEventSignaled, RunTest()); | |
476 } | |
477 | |
478 TEST_P(SwapQueueMessageTest, BitExactness) { | |
479 // Run test and verify that it did not time out. | |
480 EXPECT_EQ(kEventSignaled, RunTest()); | |
481 } | |
482 | |
483 INSTANTIATE_TEST_CASE_P( | |
484 SwapQueueMessageTestAllCombinations, | |
485 SwapQueueMessageTest, | |
486 testing::Combine(::testing::ValuesIn(kMessageQueueSizes), | |
487 ::testing::ValuesIn(kMessageQueueDataLengths))); | |
488 | |
489 INSTANTIATE_TEST_CASE_P( | |
490 SwapQueueSampleTestAllCombinations, | |
491 SwapQueueSampleTest, | |
492 testing::Combine(::testing::ValuesIn(kSampleQueueSizes), | |
493 ::testing::ValuesIn(kMaxFrameLengths), | |
494 ::testing::ValuesIn(kSampleQueueDataLengths))); | |
495 | |
496 } // namespace webrtc | |
OLD | NEW |