Chromium Code Reviews| 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 |