diff options
Diffstat (limited to 'media/libcubeb/test/test_ring_buffer.cpp')
-rw-r--r-- | media/libcubeb/test/test_ring_buffer.cpp | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/media/libcubeb/test/test_ring_buffer.cpp b/media/libcubeb/test/test_ring_buffer.cpp new file mode 100644 index 0000000000..cfaedd5048 --- /dev/null +++ b/media/libcubeb/test/test_ring_buffer.cpp @@ -0,0 +1,229 @@ +/* + * Copyright © 2016 Mozilla Foundation + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ + +#define NOMINMAX + +#include "gtest/gtest.h" +#include "cubeb_ringbuffer.h" +#include <iostream> +#include <thread> +#include <chrono> + +/* Generate a monotonically increasing sequence of numbers. */ +template<typename T> +class sequence_generator +{ +public: + sequence_generator(size_t channels) + : channels(channels) + { } + void get(T * elements, size_t frames) + { + for (size_t i = 0; i < frames; i++) { + for (size_t c = 0; c < channels; c++) { + elements[i * channels + c] = static_cast<T>(index_); + } + index_++; + } + } + void rewind(size_t frames) + { + index_ -= frames; + } +private: + size_t index_ = 0; + size_t channels = 0; +}; + +/* Checks that a sequence is monotonically increasing. */ +template<typename T> +class sequence_verifier +{ + public: + sequence_verifier(size_t channels) + : channels(channels) + { } + void check(T * elements, size_t frames) + { + for (size_t i = 0; i < frames; i++) { + for (size_t c = 0; c < channels; c++) { + if (elements[i * channels + c] != static_cast<T>(index_)) { + std::cerr << "Element " << i << " is different. Expected " + << static_cast<T>(index_) << ", got " << elements[i] + << ". (channel count: " << channels << ")." << std::endl; + ASSERT_TRUE(false); + } + } + index_++; + } + } + private: + size_t index_ = 0; + size_t channels = 0; +}; + +template<typename T> +void test_ring(lock_free_audio_ring_buffer<T>& buf, int channels, int capacity_frames) +{ + std::unique_ptr<T[]> seq(new T[capacity_frames * channels]); + sequence_generator<T> gen(channels); + sequence_verifier<T> checker(channels); + + int iterations = 1002; + + const int block_size = 128; + + while(iterations--) { + gen.get(seq.get(), block_size); + int rv = buf.enqueue(seq.get(), block_size); + ASSERT_EQ(rv, block_size); + PodZero(seq.get(), block_size); + rv = buf.dequeue(seq.get(), block_size); + ASSERT_EQ(rv, block_size); + checker.check(seq.get(), block_size); + } +} + +template<typename T> +void test_ring_multi(lock_free_audio_ring_buffer<T>& buf, int channels, int capacity_frames) +{ + sequence_verifier<T> checker(channels); + std::unique_ptr<T[]> out_buffer(new T[capacity_frames * channels]); + + const int block_size = 128; + + std::thread t([=, &buf] { + int iterations = 1002; + std::unique_ptr<T[]> in_buffer(new T[capacity_frames * channels]); + sequence_generator<T> gen(channels); + + while(iterations--) { + std::this_thread::yield(); + gen.get(in_buffer.get(), block_size); + int rv = buf.enqueue(in_buffer.get(), block_size); + ASSERT_TRUE(rv <= block_size); + if (rv != block_size) { + gen.rewind(block_size - rv); + } + } + }); + + int remaining = 1002; + + while(remaining--) { + std::this_thread::yield(); + int rv = buf.dequeue(out_buffer.get(), block_size); + ASSERT_TRUE(rv <= block_size); + checker.check(out_buffer.get(), rv); + } + + t.join(); +} + +template<typename T> +void basic_api_test(T& ring) +{ + ASSERT_EQ(ring.capacity(), 128); + + ASSERT_EQ(ring.available_read(), 0); + ASSERT_EQ(ring.available_write(), 128); + + int rv = ring.enqueue_default(63); + + ASSERT_TRUE(rv == 63); + ASSERT_EQ(ring.available_read(), 63); + ASSERT_EQ(ring.available_write(), 65); + + rv = ring.enqueue_default(65); + + ASSERT_EQ(rv, 65); + ASSERT_EQ(ring.available_read(), 128); + ASSERT_EQ(ring.available_write(), 0); + + rv = ring.dequeue(nullptr, 63); + + ASSERT_EQ(ring.available_read(), 65); + ASSERT_EQ(ring.available_write(), 63); + + rv = ring.dequeue(nullptr, 65); + + ASSERT_EQ(ring.available_read(), 0); + ASSERT_EQ(ring.available_write(), 128); +} + +void test_reset_api() { + const size_t ring_buffer_size = 128; + const size_t enqueue_size = ring_buffer_size / 2; + + lock_free_queue<float> ring(ring_buffer_size); + std::thread t([=, &ring] { + std::unique_ptr<float[]> in_buffer(new float[enqueue_size]); + ring.enqueue(in_buffer.get(), enqueue_size); + }); + + t.join(); + + ring.reset_thread_ids(); + + // Enqueue with a different thread. We have reset the thread ID + // in the ring buffer, this should work. + std::thread t2([=, &ring] { + std::unique_ptr<float[]> in_buffer(new float[enqueue_size]); + ring.enqueue(in_buffer.get(), enqueue_size); + }); + + t2.join(); + + ASSERT_TRUE(true); +} + +TEST(cubeb, ring_buffer) +{ + /* Basic API test. */ + const int min_channels = 1; + const int max_channels = 10; + const int min_capacity = 199; + const int max_capacity = 1277; + const int capacity_increment = 27; + + lock_free_queue<float> q1(128); + basic_api_test(q1); + lock_free_queue<short> q2(128); + basic_api_test(q2); + + for (size_t channels = min_channels; channels < max_channels; channels++) { + lock_free_audio_ring_buffer<float> q3(channels, 128); + basic_api_test(q3); + lock_free_audio_ring_buffer<short> q4(channels, 128); + basic_api_test(q4); + } + + /* Single thread testing. */ + /* Test mono to 9.1 */ + for (size_t channels = min_channels; channels < max_channels; channels++) { + /* Use non power-of-two numbers to catch edge-cases. */ + for (size_t capacity_frames = min_capacity; + capacity_frames < max_capacity; capacity_frames+=capacity_increment) { + lock_free_audio_ring_buffer<float> ring(channels, capacity_frames); + test_ring(ring, channels, capacity_frames); + } + } + + /* Multi thread testing */ + for (size_t channels = min_channels; channels < max_channels; channels++) { + /* Use non power-of-two numbers to catch edge-cases. */ + for (size_t capacity_frames = min_capacity; + capacity_frames < max_capacity; capacity_frames+=capacity_increment) { + lock_free_audio_ring_buffer<short> ring(channels, capacity_frames); + test_ring_multi(ring, channels, capacity_frames); + } + } + + test_reset_api(); +} + +#undef NOMINMAX |