Index: webrtc/common_audio/swap_queue_unittest.cc |
diff --git a/webrtc/common_audio/swap_queue_unittest.cc b/webrtc/common_audio/swap_queue_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e38cebd83f11ea7f1a0725b66f06a9ef211d76ef |
--- /dev/null |
+++ b/webrtc/common_audio/swap_queue_unittest.cc |
@@ -0,0 +1,496 @@ |
+/* |
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+#include <algorithm> |
+#include <map> |
+#include <tuple> |
+#include <vector> |
+ |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "webrtc/common_audio/swap_queue.h" |
+#include "webrtc/modules/interface/module_common_types.h" |
+#include "webrtc/system_wrappers/interface/event_wrapper.h" |
+#include "webrtc/system_wrappers/interface/sleep.h" |
+#include "webrtc/system_wrappers/interface/thread_wrapper.h" |
+ |
+namespace webrtc { |
+ |
+namespace { |
+ |
+// Provides thread_safe random numbers. |
+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.
|
+ public: |
+ ThreadSafeRandomNumberGenerator() : ThreadSafeRandomNumberGenerator(10000) {} |
+ explicit ThreadSafeRandomNumberGenerator(int size) { |
+ srand(42); |
+ random_numbers_.resize(size); |
+ for (int k = 0; k < size; k++) { |
+ 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
|
+ } |
+ } |
+ |
+ // Initializes a user of the random number generator and |
+ // returns a user id to use when aquiring a random number. |
+ int AddUser(int seed) { |
+ int next_user_index = GetNextIserId(); |
+ |
+ user_indices_[next_user_index] = seed % random_numbers_.size(); |
+ return next_user_index; |
+ } |
+ |
+ // Returns a random number between 0 and RAND_MAX. |
+ int Rand(int user_id) { |
+ int index = user_indices_[user_id]; |
+ index = (index < (static_cast<int>(random_numbers_.size()) - 1) ? index + 1 |
+ : 0); |
+ user_indices_[user_id] = index; |
+ return random_numbers_[index]; |
+ } |
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.
|
+ |
+ private: |
+ int GetNextIserId() { |
+ int max_user_id = -1; |
+ for (auto it = user_indices_.begin(); it != user_indices_.end(); ++it) { |
+ max_user_id = std::max(max_user_id, it->first); |
+ } |
+ |
+ return max_user_id + 1; |
+ } |
+ |
+ std::vector<int> random_numbers_; |
+ std::map<int, int> user_indices_; |
+}; |
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.
|
+ |
+// The type for a message. |
+class SwapQueueTestMessage { |
+ public: |
+ SwapQueueTestMessage() { |
+ id_ = -1; |
+ info_ = -1; |
+ } |
+ |
+ SwapQueueTestMessage(const SwapQueueTestMessage& m) |
+ : id_(m.id_), info_(m.info_) {} |
+ |
+ SwapQueueTestMessage(int id, int info) { |
+ id_ = id; |
+ info_ = info; |
+ } |
+ |
+ bool Compare(const SwapQueueTestMessage& m) { |
+ return ((id_ == m.id_) && (info_ == m.info_)); |
+ } |
+ |
+ void swap(SwapQueueTestMessage& m) { |
+ std::swap(id_, m.id_); |
+ std::swap(info_, m.info_); |
+ } |
+ |
+ private: |
+ int id_; |
+ int info_; |
+}; |
+ |
+// Implements the tests for the sample queue. |
+class SwapQueueMessageTest |
+ : public ::testing::TestWithParam<::testing::tuple<size_t, size_t>> { |
+ public: |
+ SwapQueueMessageTest() |
+ : test_complete_(EventWrapper::Create()), |
+ reader_thread_( |
+ ThreadWrapper::CreateThread(CbReaderThread, this, "reader")), |
+ writer_thread_( |
+ ThreadWrapper::CreateThread(CbWriterThread, this, "writer")) {} |
+ |
+ // Run the test with a timeout. |
+ EventTypeWrapper RunTest() { |
+ StartThreads(); |
+ return test_complete_->Wait(kTestTimeOutLimit); |
+ } |
+ |
+ virtual void SetUp() { |
+ // Read test parameter |
+ size_t queue_size = static_cast<size_t>(testing::get<0>(GetParam())); |
+ size_t messages_size = static_cast<size_t>(testing::get<1>(GetParam())); |
+ |
+ // Create queues. |
+ queue_.reset(new SwapQueue<SwapQueueTestMessage>(queue_size)); |
+ |
+ // Create and populate message vector. |
+ messages_ = std::vector<SwapQueueTestMessage>(); |
+ CreateTestData(messages_size); |
+ |
+ // Setup the random number generator. |
+ reader_rand_id_ = random_generator_.AddUser(42); |
+ writer_rand_id_ = random_generator_.AddUser(37); |
+ } |
+ |
+ virtual void TearDown() { |
+ reader_thread_->Stop(); |
+ writer_thread_->Stop(); |
+ } |
+ |
+ private: |
+ const int kTestTimeOutLimit = 10 * 1000; |
+ |
+ // Populates the data vectors with random values. |
+ void CreateTestData(int messages_size) { |
+ messages_.resize(messages_size); |
+ for (size_t k = 0; k < messages_.size(); k++) |
+ messages_[k] = |
+ SwapQueueTestMessage(random_generator_.Rand(writer_rand_id_) % 10000, |
+ random_generator_.Rand(writer_rand_id_) % 10000); |
+ } |
+ |
+ // Thread callback for the reader thread. |
+ static bool CbReaderThread(void* context) { |
+ return reinterpret_cast<SwapQueueMessageTest*>(context)->CbReaderImpl(); |
+ } |
+ |
+ // Thread callback for the writer thread. |
+ static bool CbWriterThread(void* context) { |
+ return reinterpret_cast<SwapQueueMessageTest*>(context)->CbWriterImpl(); |
+ } |
+ |
+ // Tests in a threadsafe manner whether all the samples have been |
+ // written and read. |
+ bool TestDone() { return (GetNumMessagesRead() == messages_.size()); } |
+ |
+ // Returns in a threadsafe manner the number of frames read from the queue. |
+ size_t GetNumMessagesRead() const { |
+ rtc::CritScope cs(&crit_); |
+ return num_messages_read_; |
+ } |
+ |
+ // Increases in a threadsafe manner the number of messages |
+ // read from the queue. |
+ void IncreaseMessageReadCounter() { |
+ rtc::CritScope cs(&crit_); |
+ num_messages_read_++; |
+ } |
+ |
+ // Implements the callback functionality for the reader thread. |
+ bool CbReaderImpl() { |
+ SleepRandomTime(3, reader_rand_id_); |
+ |
+ // Read the message and verify bitexactness. |
+ SwapQueueTestMessage m; |
+ if (queue_->Remove(&m)) { |
+ EXPECT_TRUE(m.Compare(messages_[GetNumMessagesRead()])); |
+ IncreaseMessageReadCounter(); |
+ } |
+ |
+ // End the test if the test is done or an assert has occurred. |
+ if (TestDone() || HasFatalFailure()) { |
+ test_complete_->Set(); |
+ } |
+ |
+ return true; |
+ } |
+ |
+ // Implements the callback functionality for the writer thread. |
+ bool CbWriterImpl() { |
+ SleepRandomTime(3, writer_rand_id_); |
+ |
+ if (num_messages_written_ < messages_.size()) { |
+ // Attempt to put the message in the queue. |
+ SwapQueueTestMessage m(messages_[num_messages_written_]); |
+ if (queue_->Insert(&m)) |
+ num_messages_written_++; |
+ } |
+ |
+ // End the test early if a fatal failure (ASSERT_*) has occurred. |
+ if (HasFatalFailure()) |
+ test_complete_->Set(); |
+ |
+ return true; |
+ } |
+ |
+ // Sleeps a random time. |
+ void SleepRandomTime(int max_sleep, int user_id) { |
+ SleepMs(random_generator_.Rand(user_id) % (max_sleep + 1)); |
+ } |
+ |
+ // Start the threads used in the test. |
+ void StartThreads() { |
+ ASSERT_TRUE(reader_thread_->Start()); |
+ reader_thread_->SetPriority(kRealtimePriority); |
+ ASSERT_TRUE(writer_thread_->Start()); |
+ writer_thread_->SetPriority(kRealtimePriority); |
+ } |
+ |
+ // Test parameters. |
+ rtc::scoped_ptr<SwapQueue<SwapQueueTestMessage>> queue_; |
+ mutable rtc::CriticalSection crit_; |
+ ThreadSafeRandomNumberGenerator random_generator_; |
+ const rtc::scoped_ptr<EventWrapper> test_complete_; |
+ rtc::scoped_ptr<ThreadWrapper> reader_thread_; |
+ rtc::scoped_ptr<ThreadWrapper> writer_thread_; |
+ int reader_rand_id_; |
+ int writer_rand_id_; |
+ std::vector<SwapQueueTestMessage> messages_; |
+ size_t num_messages_written_ = 0; |
+ size_t num_messages_read_ GUARDED_BY(crit_) = 0; |
+}; |
+ |
+// Implements the tests sample queue. |
+class SwapQueueSampleTest : public ::testing::TestWithParam< |
+ ::testing::tuple<size_t, size_t, size_t>> { |
+ public: |
+ SwapQueueSampleTest() |
+ : queue_size_(static_cast<size_t>(testing::get<0>(GetParam()))), |
+ max_frame_length_(static_cast<size_t>(testing::get<1>(GetParam()))), |
+ data_length_(static_cast<size_t>(testing::get<2>(GetParam()))), |
+ test_complete_(EventWrapper::Create()), |
+ reader_thread_( |
+ ThreadWrapper::CreateThread(CbReaderThread, this, "reader")), |
+ writer_thread_( |
+ ThreadWrapper::CreateThread(CbWriterThread, this, "writer")) {} |
+ |
+ // Run the test with a timeout. |
+ EventTypeWrapper RunTest() { |
+ StartThreads(); |
+ return test_complete_->Wait(kTestTimeOutLimit); |
+ } |
+ |
+ virtual void SetUp() { |
+ // Allocate read and write buffers. |
+ buffer_reader_ = std::vector<int16_t>(max_frame_length_, 0); |
+ buffer_writer_ = std::vector<int16_t>(max_frame_length_, 0); |
+ |
+ // Create queue. |
+ std::vector<std::vector<int16_t>> template_queue(queue_size_); |
+ for (size_t k = 0; k < queue_size_; k++) { |
+ template_queue.resize(buffer_reader_.size()); |
+ } |
+ sample_queue_.reset(new SwapQueue<std::vector<int16_t>>(&template_queue)); |
+ |
+ // Create and populate data vectors. |
+ CreateTestData(data_length_); |
+ |
+ // Setup the random number generator. |
+ reader_rand_id_ = random_generator_.AddUser(42); |
+ writer_rand_id_ = random_generator_.AddUser(37); |
+ } |
+ |
+ virtual void TearDown() { |
+ reader_thread_->Stop(); |
+ writer_thread_->Stop(); |
+ } |
+ |
+ private: |
+ const int kTestTimeOutLimit = 10 * 1000; |
+ |
+ // Populates the data vectors with random values. |
+ void CreateTestData(size_t data_length) { |
+ samples_.resize(data_length); |
+ for (size_t k = 0; k < data_length; k++) { |
+ samples_[k] = |
+ ((random_generator_.Rand(writer_rand_id_) % (32767 + 32768)) - 32768); |
+ } |
+ } |
+ |
+ // Thread callback for the reader thread. |
+ static bool CbReaderThread(void* context) { |
+ return reinterpret_cast<SwapQueueSampleTest*>(context)->CbReaderImpl(); |
+ } |
+ |
+ // Thread callback for the writer thread. |
+ static bool CbWriterThread(void* context) { |
+ return reinterpret_cast<SwapQueueSampleTest*>(context)->CbWriterImpl(); |
+ } |
+ |
+ // Tests in a threadsafe manner whether all the samples have been |
+ // written and read. |
+ bool TestDone() { return (GetNumSamplesRead() == data_length_); } |
+ |
+ // Returns in a threadsafe manner the number of frames read from the queue. |
+ size_t GetNumFramesRead() { |
+ rtc::CritScope cs(&crit_); |
+ return num_frames_read_; |
+ } |
+ |
+ // Returns in a threadsafe manner the number of samples read from the queue. |
+ size_t GetNumSamplesRead() { |
+ rtc::CritScope cs(&crit_); |
+ return num_samples_read_; |
+ } |
+ |
+ // Increases in a threadsafe manner the number of frames and samples |
+ // read from the queue. |
+ void IncreaseReadCounters(size_t num_samples_read) { |
+ rtc::CritScope cs(&crit_); |
+ num_frames_read_++; |
+ num_samples_read_ += num_samples_read; |
+ } |
+ |
+ // Implements the callback functionality for the reader thread. |
+ bool CbReaderImpl() { |
+ SleepRandomTime(3, reader_rand_id_); |
+ |
+ // Read the samples and verify bitexactness. |
+ const size_t num_samples_read = GetNumSamplesRead(); |
+ if (sample_queue_->Remove(&buffer_reader_)) { |
+ for (size_t k = 0; k < buffer_reader_.size(); k++) |
+ EXPECT_EQ(buffer_reader_[k], samples_[num_samples_read + k]); |
+ |
+ IncreaseReadCounters(buffer_reader_.size()); |
+ } |
+ |
+ // End the test if the test is done or an assert has occurred. |
+ if (TestDone() || HasFatalFailure()) { |
+ test_complete_->Set(); |
+ } |
+ |
+ return true; |
+ } |
+ |
+ // Implements the callback functionality for the writer thread. |
+ bool CbWriterImpl() { |
+ SleepRandomTime(3, writer_rand_id_); |
+ |
+ // Choose number of samples to write. |
+ const size_t num_samples_to_write = |
+ std::min(random_generator_.Rand(writer_rand_id_) % max_frame_length_, |
+ data_length_ - num_samples_written_); |
+ |
+ // Write the data. |
+ bool data_written = false; |
+ if (num_samples_to_write > 0) { |
+ if (buffer_writer_.size() != num_samples_to_write) |
+ buffer_writer_.resize(num_samples_to_write); |
+ |
+ memcpy(&buffer_writer_[0], &samples_[num_samples_written_], |
+ num_samples_to_write * sizeof(samples_[0])); |
+ |
+ data_written = sample_queue_->Insert(&buffer_writer_); |
+ } |
+ |
+ // Update the number of samples left to write |
+ if (data_written) { |
+ num_samples_written_ += num_samples_to_write; |
+ num_frames_written_++; |
+ } |
+ |
+ // End the test early if a fatal failure (ASSERT_*) has occurred. |
+ if (HasFatalFailure()) |
+ test_complete_->Set(); |
+ |
+ return true; |
+ } |
+ |
+ // Sleeps a random time. |
+ void SleepRandomTime(int max_sleep, int user_id) { |
+ SleepMs(random_generator_.Rand(user_id) % (max_sleep + 1)); |
+ } |
+ |
+ // Start the threads used in the test. |
+ void StartThreads() { |
+ ASSERT_TRUE(reader_thread_->Start()); |
+ reader_thread_->SetPriority(kRealtimePriority); |
+ ASSERT_TRUE(writer_thread_->Start()); |
+ writer_thread_->SetPriority(kRealtimePriority); |
+ } |
+ |
+ // Test parameters. |
+ const size_t queue_size_; |
+ const size_t max_frame_length_; |
+ const size_t data_length_; |
+ |
+ rtc::scoped_ptr<SwapQueue<std::vector<int16_t>>> sample_queue_; |
+ rtc::CriticalSection crit_; |
+ ThreadSafeRandomNumberGenerator random_generator_; |
+ const rtc::scoped_ptr<EventWrapper> test_complete_; |
+ rtc::scoped_ptr<ThreadWrapper> reader_thread_; |
+ rtc::scoped_ptr<ThreadWrapper> writer_thread_; |
+ std::vector<int16_t> samples_; |
+ size_t num_samples_read_ GUARDED_BY(crit_) = 0; |
+ size_t num_samples_written_ = 0; |
+ int reader_rand_id_; |
+ int writer_rand_id_; |
+ std::vector<int16_t> buffer_reader_; |
+ std::vector<int16_t> buffer_writer_; |
+ size_t num_frames_written_ = 0; |
+ size_t num_frames_read_ GUARDED_BY(crit_) = 0; |
+}; |
+ |
+// Test parameters for the message queue tests. |
+const size_t kMessageQueueSizes[] = {2, 7, 20}; |
+const size_t kMessageQueueDataLengths[] = {100}; |
+ |
+// Test parameters for the sample queue tests. |
+const size_t kSampleQueueSizes[] = {7, 100}; |
+const size_t kMaxFrameLengths[] = {77, 160}; |
+const size_t kSampleQueueDataLengths[] = {200, 500}; |
+ |
+} // anonymous namespace |
+ |
+TEST(SwapQueueTest, FullQueue) { |
+ SwapQueue<int> queue(2); |
+ int i = 0; |
+ EXPECT_TRUE(queue.Insert(&i)); |
+ EXPECT_TRUE(queue.Insert(&i)); |
+ EXPECT_FALSE(queue.Insert(&i)); |
+ EXPECT_TRUE(queue.Remove(&i)); |
+ EXPECT_TRUE(queue.Insert(&i)); |
+ EXPECT_FALSE(queue.Insert(&i)); |
+} |
+ |
+TEST(SwapQueueTest, EmptyQueue) { |
+ SwapQueue<int> queue(2); |
+ int i = 0; |
+ EXPECT_FALSE(queue.Remove(&i)); |
+ EXPECT_TRUE(queue.Insert(&i)); |
+ EXPECT_TRUE(queue.Remove(&i)); |
+ EXPECT_FALSE(queue.Remove(&i)); |
+} |
+ |
+TEST(SwapQueueTest, InitializeTest) { |
+ const size_t kQueueSize = 3; |
+ std::vector<std::vector<int>> v(2); |
+ v[0].resize(kQueueSize); |
+ v[1].resize(kQueueSize); |
+ SwapQueue<std::vector<int>> queue(&v); |
+ std::vector<int> i(kQueueSize, 0); |
+ |
+ EXPECT_TRUE(queue.Insert(&i)); |
+ EXPECT_EQ(i.size(), kQueueSize); |
+ EXPECT_TRUE(queue.Insert(&i)); |
+ EXPECT_EQ(i.size(), kQueueSize); |
+ EXPECT_TRUE(queue.Remove(&i)); |
+ EXPECT_EQ(i.size(), kQueueSize); |
+ EXPECT_TRUE(queue.Remove(&i)); |
+ EXPECT_EQ(i.size(), kQueueSize); |
+} |
+ |
+TEST_P(SwapQueueSampleTest, BitExactness) { |
+ // Run test and verify that it did not time out. |
+ EXPECT_EQ(kEventSignaled, RunTest()); |
+} |
+ |
+TEST_P(SwapQueueMessageTest, BitExactness) { |
+ // Run test and verify that it did not time out. |
+ EXPECT_EQ(kEventSignaled, RunTest()); |
+} |
+ |
+INSTANTIATE_TEST_CASE_P( |
+ SwapQueueMessageTestAllCombinations, |
+ SwapQueueMessageTest, |
+ testing::Combine(::testing::ValuesIn(kMessageQueueSizes), |
+ ::testing::ValuesIn(kMessageQueueDataLengths))); |
+ |
+INSTANTIATE_TEST_CASE_P( |
+ SwapQueueSampleTestAllCombinations, |
+ SwapQueueSampleTest, |
+ testing::Combine(::testing::ValuesIn(kSampleQueueSizes), |
+ ::testing::ValuesIn(kMaxFrameLengths), |
+ ::testing::ValuesIn(kSampleQueueDataLengths))); |
+ |
+} // namespace webrtc |