You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
927 lines
34 KiB
927 lines
34 KiB
/*
|
|
* Copyright 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "os/queue.h"
|
|
|
|
#include <sys/eventfd.h>
|
|
|
|
#include <atomic>
|
|
#include <future>
|
|
#include <unordered_map>
|
|
|
|
#include "common/bind.h"
|
|
#include "gtest/gtest.h"
|
|
#include "os/reactor.h"
|
|
|
|
namespace bluetooth {
|
|
namespace os {
|
|
namespace {
|
|
|
|
constexpr int kQueueSize = 10;
|
|
constexpr int kHalfOfQueueSize = kQueueSize / 2;
|
|
constexpr int kDoubleOfQueueSize = kQueueSize * 2;
|
|
constexpr int kQueueSizeOne = 1;
|
|
|
|
class QueueTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
enqueue_thread_ = new Thread("enqueue_thread", Thread::Priority::NORMAL);
|
|
enqueue_handler_ = new Handler(enqueue_thread_);
|
|
dequeue_thread_ = new Thread("dequeue_thread", Thread::Priority::NORMAL);
|
|
dequeue_handler_ = new Handler(dequeue_thread_);
|
|
}
|
|
void TearDown() override {
|
|
enqueue_handler_->Clear();
|
|
delete enqueue_handler_;
|
|
delete enqueue_thread_;
|
|
dequeue_handler_->Clear();
|
|
delete dequeue_handler_;
|
|
delete dequeue_thread_;
|
|
enqueue_handler_ = nullptr;
|
|
enqueue_thread_ = nullptr;
|
|
dequeue_handler_ = nullptr;
|
|
dequeue_thread_ = nullptr;
|
|
}
|
|
|
|
Thread* enqueue_thread_;
|
|
Handler* enqueue_handler_;
|
|
Thread* dequeue_thread_;
|
|
Handler* dequeue_handler_;
|
|
};
|
|
|
|
class TestEnqueueEnd {
|
|
public:
|
|
explicit TestEnqueueEnd(Queue<std::string>* queue, Handler* handler)
|
|
: count(0), handler_(handler), queue_(queue), delay_(0) {}
|
|
|
|
~TestEnqueueEnd() {}
|
|
|
|
void RegisterEnqueue(std::unordered_map<int, std::promise<int>>* promise_map) {
|
|
promise_map_ = promise_map;
|
|
handler_->Post(common::BindOnce(&TestEnqueueEnd::handle_register_enqueue, common::Unretained(this)));
|
|
}
|
|
|
|
void UnregisterEnqueue() {
|
|
std::promise<void> promise;
|
|
auto future = promise.get_future();
|
|
|
|
handler_->Post(
|
|
common::BindOnce(&TestEnqueueEnd::handle_unregister_enqueue, common::Unretained(this), std::move(promise)));
|
|
future.wait();
|
|
}
|
|
|
|
std::unique_ptr<std::string> EnqueueCallbackForTest() {
|
|
if (delay_ != 0) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(delay_));
|
|
}
|
|
|
|
count++;
|
|
std::unique_ptr<std::string> data = std::move(buffer_.front());
|
|
buffer_.pop();
|
|
std::string copy = *data;
|
|
if (buffer_.empty()) {
|
|
queue_->UnregisterEnqueue();
|
|
}
|
|
|
|
auto pair = promise_map_->find(buffer_.size());
|
|
if (pair != promise_map_->end()) {
|
|
pair->second.set_value(pair->first);
|
|
promise_map_->erase(pair->first);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void setDelay(int value) {
|
|
delay_ = value;
|
|
}
|
|
|
|
std::queue<std::unique_ptr<std::string>> buffer_;
|
|
int count;
|
|
|
|
private:
|
|
Handler* handler_;
|
|
Queue<std::string>* queue_;
|
|
std::unordered_map<int, std::promise<int>>* promise_map_;
|
|
int delay_;
|
|
|
|
void handle_register_enqueue() {
|
|
queue_->RegisterEnqueue(handler_, common::Bind(&TestEnqueueEnd::EnqueueCallbackForTest, common::Unretained(this)));
|
|
}
|
|
|
|
void handle_unregister_enqueue(std::promise<void> promise) {
|
|
queue_->UnregisterEnqueue();
|
|
promise.set_value();
|
|
}
|
|
};
|
|
|
|
class TestDequeueEnd {
|
|
public:
|
|
explicit TestDequeueEnd(Queue<std::string>* queue, Handler* handler, int capacity)
|
|
: count(0), handler_(handler), queue_(queue), capacity_(capacity), delay_(0) {}
|
|
|
|
~TestDequeueEnd() {}
|
|
|
|
void RegisterDequeue(std::unordered_map<int, std::promise<int>>* promise_map) {
|
|
promise_map_ = promise_map;
|
|
handler_->Post(common::BindOnce(&TestDequeueEnd::handle_register_dequeue, common::Unretained(this)));
|
|
}
|
|
|
|
void UnregisterDequeue() {
|
|
std::promise<void> promise;
|
|
auto future = promise.get_future();
|
|
|
|
handler_->Post(
|
|
common::BindOnce(&TestDequeueEnd::handle_unregister_dequeue, common::Unretained(this), std::move(promise)));
|
|
future.wait();
|
|
}
|
|
|
|
void DequeueCallbackForTest() {
|
|
if (delay_ != 0) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(delay_));
|
|
}
|
|
|
|
count++;
|
|
std::unique_ptr<std::string> data = queue_->TryDequeue();
|
|
buffer_.push(std::move(data));
|
|
|
|
if (buffer_.size() == capacity_) {
|
|
queue_->UnregisterDequeue();
|
|
}
|
|
|
|
auto pair = promise_map_->find(buffer_.size());
|
|
if (pair != promise_map_->end()) {
|
|
pair->second.set_value(pair->first);
|
|
promise_map_->erase(pair->first);
|
|
}
|
|
}
|
|
|
|
void setDelay(int value) {
|
|
delay_ = value;
|
|
}
|
|
|
|
std::queue<std::unique_ptr<std::string>> buffer_;
|
|
int count;
|
|
|
|
private:
|
|
Handler* handler_;
|
|
Queue<std::string>* queue_;
|
|
std::unordered_map<int, std::promise<int>>* promise_map_;
|
|
int capacity_;
|
|
int delay_;
|
|
|
|
void handle_register_dequeue() {
|
|
queue_->RegisterDequeue(handler_, common::Bind(&TestDequeueEnd::DequeueCallbackForTest, common::Unretained(this)));
|
|
}
|
|
|
|
void handle_unregister_dequeue(std::promise<void> promise) {
|
|
queue_->UnregisterDequeue();
|
|
promise.set_value();
|
|
}
|
|
};
|
|
|
|
// Enqueue end level : 0 -> queue is full, 1 - > queue isn't full
|
|
// Dequeue end level : 0 -> queue is empty, 1 - > queue isn't empty
|
|
|
|
// Test 1 : Queue is empty
|
|
|
|
// Enqueue end level : 1
|
|
// Dequeue end level : 0
|
|
// Test 1-1 EnqueueCallback should continually be invoked when queue isn't full
|
|
TEST_F(QueueTest, register_enqueue_with_empty_queue) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
|
|
// Push kQueueSize data to enqueue_end buffer
|
|
for (int i = 0; i < kQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kQueueSize);
|
|
|
|
// Register enqueue and expect data move to Queue
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
}
|
|
|
|
// Enqueue end level : 1
|
|
// Dequeue end level : 0
|
|
// Test 1-2 DequeueCallback shouldn't be invoked when queue is empty
|
|
TEST_F(QueueTest, register_dequeue_with_empty_queue) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
|
|
|
|
// Register dequeue, DequeueCallback shouldn't be invoked
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
EXPECT_EQ(test_dequeue_end.count, 0);
|
|
|
|
test_dequeue_end.UnregisterDequeue();
|
|
}
|
|
|
|
// Test 2 : Queue is full
|
|
|
|
// Enqueue end level : 0
|
|
// Dequeue end level : 1
|
|
// Test 2-1 EnqueueCallback shouldn't be invoked when queue is full
|
|
TEST_F(QueueTest, register_enqueue_with_full_queue) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
|
|
// make Queue full
|
|
for (int i = 0; i < kQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
|
|
// push some data to enqueue_end buffer and register enqueue;
|
|
for (int i = 0; i < kHalfOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
|
|
// EnqueueCallback shouldn't be invoked
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kHalfOfQueueSize);
|
|
EXPECT_EQ(test_enqueue_end.count, kQueueSize);
|
|
|
|
test_enqueue_end.UnregisterEnqueue();
|
|
}
|
|
|
|
// Enqueue end level : 0
|
|
// Dequeue end level : 1
|
|
// Test 2-2 DequeueCallback should continually be invoked when queue isn't empty
|
|
TEST_F(QueueTest, register_dequeue_with_full_queue) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
|
|
|
|
// make Queue full
|
|
for (int i = 0; i < kQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
|
|
// Register dequeue and expect data move to dequeue end buffer
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
|
|
auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
dequeue_future.wait();
|
|
EXPECT_EQ(dequeue_future.get(), kQueueSize);
|
|
|
|
test_dequeue_end.UnregisterDequeue();
|
|
}
|
|
|
|
// Test 3 : Queue is non-empty and non-full
|
|
|
|
// Enqueue end level : 1
|
|
// Dequeue end level : 1
|
|
// Test 3-1 Register enqueue with half empty queue, EnqueueCallback should continually be invoked
|
|
TEST_F(QueueTest, register_enqueue_with_half_empty_queue) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
|
|
// make Queue half empty
|
|
for (int i = 0; i < kHalfOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
|
|
// push some data to enqueue_end buffer and register enqueue;
|
|
for (int i = 0; i < kHalfOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
|
|
// Register enqueue and expect data move to Queue
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
}
|
|
|
|
// Enqueue end level : 1
|
|
// Dequeue end level : 1
|
|
// Test 3-2 Register dequeue with half empty queue, DequeueCallback should continually be invoked
|
|
TEST_F(QueueTest, register_dequeue_with_half_empty_queue) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
|
|
|
|
// make Queue half empty
|
|
for (int i = 0; i < kHalfOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
|
|
// Register dequeue and expect data move to dequeue end buffer
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
dequeue_promise_map.emplace(
|
|
std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize), std::forward_as_tuple());
|
|
auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
dequeue_future.wait();
|
|
EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
|
|
|
|
test_dequeue_end.UnregisterDequeue();
|
|
}
|
|
|
|
// Dynamic level test
|
|
|
|
// Test 4 : Queue becomes full during test, EnqueueCallback should stop to be invoked
|
|
|
|
// Enqueue end level : 1 -> 0
|
|
// Dequeue end level : 1
|
|
// Test 4-1 Queue becomes full due to only register EnqueueCallback
|
|
TEST_F(QueueTest, queue_becomes_full_enqueue_callback_only) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
|
|
// push double of kQueueSize to enqueue end buffer
|
|
for (int i = 0; i < kDoubleOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
|
|
// Register enqueue and expect kQueueSize data move to Queue
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[kQueueSize].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), kQueueSize);
|
|
|
|
// EnqueueCallback shouldn't be invoked and buffer size stay in kQueueSize
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kQueueSize);
|
|
EXPECT_EQ(test_enqueue_end.count, kQueueSize);
|
|
|
|
test_enqueue_end.UnregisterEnqueue();
|
|
}
|
|
|
|
// Enqueue end level : 1 -> 0
|
|
// Dequeue end level : 1
|
|
// Test 4-2 Queue becomes full due to DequeueCallback unregister during test
|
|
TEST_F(QueueTest, queue_becomes_full_dequeue_callback_unregister) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kHalfOfQueueSize);
|
|
|
|
// push double of kQueueSize to enqueue end buffer
|
|
for (int i = 0; i < kDoubleOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
|
|
// Register dequeue
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
dequeue_promise_map.emplace(
|
|
std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize), std::forward_as_tuple());
|
|
auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
|
|
// Register enqueue
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(
|
|
std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[kHalfOfQueueSize].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
|
|
// Dequeue end will unregister when buffer size is kHalfOfQueueSize
|
|
dequeue_future.wait();
|
|
EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
|
|
|
|
// EnqueueCallback shouldn't be invoked and buffer size stay in kHalfOfQueueSize
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), kHalfOfQueueSize);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kHalfOfQueueSize);
|
|
EXPECT_EQ(test_enqueue_end.count, kQueueSize + kHalfOfQueueSize);
|
|
|
|
test_enqueue_end.UnregisterEnqueue();
|
|
}
|
|
|
|
// Enqueue end level : 1 -> 0
|
|
// Dequeue end level : 1
|
|
// Test 4-3 Queue becomes full due to DequeueCallback is slower
|
|
TEST_F(QueueTest, queue_becomes_full_dequeue_callback_slower) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
|
|
|
|
// push double of kDoubleOfQueueSize to enqueue end buffer
|
|
for (int i = 0; i < kDoubleOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
|
|
// Set 20 ms delay for callback and register dequeue
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
test_dequeue_end.setDelay(20);
|
|
auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
|
|
// Register enqueue
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
|
|
// Wait for enqueue buffer empty and expect queue is full
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
EXPECT_GE(test_dequeue_end.buffer_.size(), kQueueSize - 1);
|
|
|
|
test_dequeue_end.UnregisterDequeue();
|
|
}
|
|
|
|
// Enqueue end level : 0 -> 1
|
|
// Dequeue end level : 1 -> 0
|
|
// Test 5 Queue becomes full and non empty at same time.
|
|
TEST_F(QueueTest, queue_becomes_full_and_non_empty_at_same_time) {
|
|
Queue<std::string> queue(kQueueSizeOne);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
|
|
|
|
// push double of kQueueSize to enqueue end buffer
|
|
for (int i = 0; i < kQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
|
|
// Register dequeue
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
|
|
auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
|
|
// Register enqueue
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
|
|
// Wait for all data move from enqueue end buffer to dequeue end buffer
|
|
dequeue_future.wait();
|
|
EXPECT_EQ(dequeue_future.get(), kQueueSize);
|
|
|
|
test_dequeue_end.UnregisterDequeue();
|
|
}
|
|
|
|
// Enqueue end level : 1 -> 0
|
|
// Dequeue end level : 1
|
|
// Test 6 Queue becomes not full during test, EnqueueCallback should start to be invoked
|
|
TEST_F(QueueTest, queue_becomes_non_full_during_test) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize * 3);
|
|
|
|
// make Queue full
|
|
for (int i = 0; i < kDoubleOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[kQueueSize].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), kQueueSize);
|
|
|
|
// Expect kQueueSize data block in enqueue end buffer
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
EXPECT_EQ(test_enqueue_end.buffer_.size(), kQueueSize);
|
|
|
|
// Register dequeue
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
|
|
// Expect enqueue end will empty
|
|
enqueue_future = enqueue_promise_map[0].get_future();
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
|
|
test_dequeue_end.UnregisterDequeue();
|
|
}
|
|
|
|
// Enqueue end level : 0 -> 1
|
|
// Dequeue end level : 1 -> 0
|
|
// Test 7 Queue becomes non full and empty at same time. (Exactly same as Test 5)
|
|
TEST_F(QueueTest, queue_becomes_non_full_and_empty_at_same_time) {
|
|
Queue<std::string> queue(kQueueSizeOne);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
|
|
|
|
// push double of kQueueSize to enqueue end buffer
|
|
for (int i = 0; i < kQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
|
|
// Register dequeue
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
|
|
auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
|
|
// Register enqueue
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
|
|
// Wait for all data move from enqueue end buffer to dequeue end buffer
|
|
dequeue_future.wait();
|
|
EXPECT_EQ(dequeue_future.get(), kQueueSize);
|
|
|
|
test_dequeue_end.UnregisterDequeue();
|
|
}
|
|
|
|
// Test 8 : Queue becomes empty during test, DequeueCallback should stop to be invoked
|
|
|
|
// Enqueue end level : 1
|
|
// Dequeue end level : 1 -> 0
|
|
// Test 8-1 Queue becomes empty due to only register DequeueCallback
|
|
TEST_F(QueueTest, queue_becomes_empty_dequeue_callback_only) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kHalfOfQueueSize);
|
|
|
|
// make Queue half empty
|
|
for (int i = 0; i < kHalfOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
|
|
// Register dequeue, expect kHalfOfQueueSize data move to dequeue end buffer
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
dequeue_promise_map.emplace(
|
|
std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize), std::forward_as_tuple());
|
|
auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
dequeue_future.wait();
|
|
EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
|
|
|
|
// Expect DequeueCallback should stop to be invoked
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
EXPECT_EQ(test_dequeue_end.count, kHalfOfQueueSize);
|
|
}
|
|
|
|
// Enqueue end level : 1
|
|
// Dequeue end level : 1 -> 0
|
|
// Test 8-2 Queue becomes empty due to EnqueueCallback unregister during test
|
|
TEST_F(QueueTest, queue_becomes_empty_enqueue_callback_unregister) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
|
|
|
|
// make Queue half empty
|
|
for (int i = 0; i < kHalfOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
|
|
auto enqueue_future = enqueue_promise_map[0].get_future();
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
enqueue_future.wait();
|
|
EXPECT_EQ(enqueue_future.get(), 0);
|
|
|
|
// push kHalfOfQueueSize to enqueue end buffer and register enqueue.
|
|
for (int i = 0; i < kHalfOfQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
|
|
// Register dequeue, expect kQueueSize move to dequeue end buffer
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
|
|
auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
dequeue_future.wait();
|
|
EXPECT_EQ(dequeue_future.get(), kQueueSize);
|
|
|
|
// Expect DequeueCallback should stop to be invoked
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
EXPECT_EQ(test_dequeue_end.count, kQueueSize);
|
|
}
|
|
|
|
// Enqueue end level : 1
|
|
// Dequeue end level : 0 -> 1
|
|
// Test 9 Queue becomes not empty during test, DequeueCallback should start to be invoked
|
|
TEST_F(QueueTest, queue_becomes_non_empty_during_test) {
|
|
Queue<std::string> queue(kQueueSize);
|
|
TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
|
|
TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
|
|
|
|
// Register dequeue
|
|
std::unordered_map<int, std::promise<int>> dequeue_promise_map;
|
|
dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
|
|
test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
|
|
|
|
// push kQueueSize data to enqueue end buffer and register enqueue
|
|
for (int i = 0; i < kQueueSize; i++) {
|
|
std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
|
|
test_enqueue_end.buffer_.push(std::move(data));
|
|
}
|
|
std::unordered_map<int, std::promise<int>> enqueue_promise_map;
|
|
test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
|
|
|
|
// Expect kQueueSize data move to dequeue end buffer
|
|
auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
|
|
dequeue_future.wait();
|
|
EXPECT_EQ(dequeue_future.get(), kQueueSize);
|
|
}
|
|
|
|
TEST_F(QueueTest, pass_smart_pointer_and_unregister) {
|
|
Queue<std::string>* queue = new Queue<std::string>(kQueueSize);
|
|
|
|
// Enqueue a string
|
|
std::string valid = "Valid String";
|
|
std::shared_ptr<std::string> shared = std::make_shared<std::string>(valid);
|
|
queue->RegisterEnqueue(
|
|
enqueue_handler_,
|
|
common::Bind(
|
|
[](Queue<std::string>* queue, std::shared_ptr<std::string> shared) {
|
|
queue->UnregisterEnqueue();
|
|
return std::make_unique<std::string>(*shared);
|
|
},
|
|
common::Unretained(queue),
|
|
shared));
|
|
|
|
// Dequeue the string
|
|
queue->RegisterDequeue(
|
|
dequeue_handler_,
|
|
common::Bind(
|
|
[](Queue<std::string>* queue, std::string valid) {
|
|
queue->UnregisterDequeue();
|
|
auto answer = *queue->TryDequeue();
|
|
ASSERT_EQ(answer, valid);
|
|
},
|
|
common::Unretained(queue),
|
|
valid));
|
|
|
|
// Wait for both handlers to finish and delete the Queue
|
|
std::promise<void> promise;
|
|
auto future = promise.get_future();
|
|
|
|
enqueue_handler_->Post(common::BindOnce(
|
|
[](os::Handler* dequeue_handler, Queue<std::string>* queue, std::promise<void>* promise) {
|
|
dequeue_handler->Post(common::BindOnce(
|
|
[](Queue<std::string>* queue, std::promise<void>* promise) {
|
|
delete queue;
|
|
promise->set_value();
|
|
},
|
|
common::Unretained(queue),
|
|
common::Unretained(promise)));
|
|
},
|
|
common::Unretained(dequeue_handler_),
|
|
common::Unretained(queue),
|
|
common::Unretained(&promise)));
|
|
future.wait();
|
|
}
|
|
|
|
std::unique_ptr<std::string> sleep_and_enqueue_callback(int* to_increase) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
(*to_increase)++;
|
|
return std::make_unique<std::string>("Hello");
|
|
}
|
|
|
|
TEST_F(QueueTest, unregister_enqueue_and_wait) {
|
|
Queue<std::string> queue(10);
|
|
int* indicator = new int(100);
|
|
queue.RegisterEnqueue(enqueue_handler_, common::Bind(&sleep_and_enqueue_callback, common::Unretained(indicator)));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
queue.UnregisterEnqueue();
|
|
EXPECT_EQ(*indicator, 101);
|
|
delete indicator;
|
|
}
|
|
|
|
std::unique_ptr<std::string> sleep_and_enqueue_callback_and_unregister(
|
|
int* to_increase, Queue<std::string>* queue, std::atomic_bool* is_registered) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
(*to_increase)++;
|
|
if (is_registered->exchange(false)) {
|
|
queue->UnregisterEnqueue();
|
|
}
|
|
return std::make_unique<std::string>("Hello");
|
|
}
|
|
|
|
TEST_F(QueueTest, unregister_enqueue_and_wait_maybe_unregistered) {
|
|
Queue<std::string> queue(10);
|
|
int* indicator = new int(100);
|
|
std::atomic_bool is_registered = true;
|
|
queue.RegisterEnqueue(
|
|
enqueue_handler_,
|
|
common::Bind(
|
|
&sleep_and_enqueue_callback_and_unregister,
|
|
common::Unretained(indicator),
|
|
common::Unretained(&queue),
|
|
common::Unretained(&is_registered)));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
if (is_registered.exchange(false)) {
|
|
queue.UnregisterEnqueue();
|
|
}
|
|
EXPECT_EQ(*indicator, 101);
|
|
delete indicator;
|
|
}
|
|
|
|
void sleep_and_dequeue_callback(int* to_increase) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
(*to_increase)++;
|
|
}
|
|
|
|
TEST_F(QueueTest, unregister_dequeue_and_wait) {
|
|
int* indicator = new int(100);
|
|
Queue<std::string> queue(10);
|
|
queue.RegisterEnqueue(
|
|
enqueue_handler_,
|
|
common::Bind(
|
|
[](Queue<std::string>* queue) {
|
|
queue->UnregisterEnqueue();
|
|
return std::make_unique<std::string>("Hello");
|
|
},
|
|
common::Unretained(&queue)));
|
|
queue.RegisterDequeue(enqueue_handler_, common::Bind(&sleep_and_dequeue_callback, common::Unretained(indicator)));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
queue.UnregisterDequeue();
|
|
EXPECT_EQ(*indicator, 101);
|
|
delete indicator;
|
|
}
|
|
|
|
// Create all threads for death tests in the function that dies
|
|
class QueueDeathTest : public ::testing::Test {
|
|
public:
|
|
void RegisterEnqueueAndDelete() {
|
|
Thread* enqueue_thread = new Thread("enqueue_thread", Thread::Priority::NORMAL);
|
|
Handler* enqueue_handler = new Handler(enqueue_thread);
|
|
Queue<std::string>* queue = new Queue<std::string>(kQueueSizeOne);
|
|
queue->RegisterEnqueue(
|
|
enqueue_handler, common::Bind([]() { return std::make_unique<std::string>("A string to fill the queue"); }));
|
|
delete queue;
|
|
}
|
|
|
|
void RegisterDequeueAndDelete() {
|
|
Thread* dequeue_thread = new Thread("dequeue_thread", Thread::Priority::NORMAL);
|
|
Handler* dequeue_handler = new Handler(dequeue_thread);
|
|
Queue<std::string>* queue = new Queue<std::string>(kQueueSizeOne);
|
|
queue->RegisterDequeue(
|
|
dequeue_handler,
|
|
common::Bind([](Queue<std::string>* queue) { queue->TryDequeue(); }, common::Unretained(queue)));
|
|
delete queue;
|
|
}
|
|
};
|
|
|
|
TEST_F(QueueDeathTest, die_if_enqueue_not_unregistered) {
|
|
EXPECT_DEATH(RegisterEnqueueAndDelete(), "nqueue");
|
|
}
|
|
|
|
TEST_F(QueueDeathTest, die_if_dequeue_not_unregistered) {
|
|
EXPECT_DEATH(RegisterDequeueAndDelete(), "equeue");
|
|
}
|
|
|
|
class MockIQueueEnqueue : public IQueueEnqueue<int> {
|
|
public:
|
|
void RegisterEnqueue(Handler* handler, EnqueueCallback callback) override {
|
|
EXPECT_FALSE(registered_);
|
|
registered_ = true;
|
|
handler->Post(common::BindOnce(&MockIQueueEnqueue::handle_register_enqueue, common::Unretained(this), callback));
|
|
}
|
|
|
|
void handle_register_enqueue(EnqueueCallback callback) {
|
|
if (dont_handle_register_enqueue_) {
|
|
return;
|
|
}
|
|
while (registered_) {
|
|
std::unique_ptr<int> front = callback.Run();
|
|
queue_.push(*front);
|
|
}
|
|
}
|
|
|
|
void UnregisterEnqueue() override {
|
|
EXPECT_TRUE(registered_);
|
|
registered_ = false;
|
|
}
|
|
|
|
bool dont_handle_register_enqueue_ = false;
|
|
bool registered_ = false;
|
|
std::queue<int> queue_;
|
|
};
|
|
|
|
class EnqueueBufferTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
|
|
handler_ = new Handler(thread_);
|
|
}
|
|
|
|
void TearDown() override {
|
|
handler_->Clear();
|
|
delete handler_;
|
|
delete thread_;
|
|
}
|
|
|
|
void SynchronizeHandler() {
|
|
std::promise<void> promise;
|
|
auto future = promise.get_future();
|
|
handler_->Post(common::BindOnce([](std::promise<void> promise) { promise.set_value(); }, std::move(promise)));
|
|
future.wait();
|
|
}
|
|
|
|
MockIQueueEnqueue enqueue_;
|
|
EnqueueBuffer<int> enqueue_buffer_{&enqueue_};
|
|
Thread* thread_;
|
|
Handler* handler_;
|
|
};
|
|
|
|
TEST_F(EnqueueBufferTest, enqueue) {
|
|
int num_items = 10;
|
|
for (int i = 0; i < num_items; i++) {
|
|
enqueue_buffer_.Enqueue(std::make_unique<int>(i), handler_);
|
|
}
|
|
SynchronizeHandler();
|
|
for (int i = 0; i < num_items; i++) {
|
|
ASSERT_EQ(enqueue_.queue_.front(), i);
|
|
enqueue_.queue_.pop();
|
|
}
|
|
ASSERT_FALSE(enqueue_.registered_);
|
|
}
|
|
|
|
TEST_F(EnqueueBufferTest, clear) {
|
|
enqueue_.dont_handle_register_enqueue_ = true;
|
|
int num_items = 10;
|
|
for (int i = 0; i < num_items; i++) {
|
|
enqueue_buffer_.Enqueue(std::make_unique<int>(i), handler_);
|
|
}
|
|
ASSERT_TRUE(enqueue_.registered_);
|
|
enqueue_buffer_.Clear();
|
|
ASSERT_FALSE(enqueue_.registered_);
|
|
}
|
|
|
|
TEST_F(EnqueueBufferTest, delete_when_in_callback) {
|
|
Queue<int>* queue = new Queue<int>(kQueueSize);
|
|
EnqueueBuffer<int>* enqueue_buffer = new EnqueueBuffer<int>(queue);
|
|
int num_items = 10;
|
|
for (int i = 0; i < num_items; i++) {
|
|
enqueue_buffer->Enqueue(std::make_unique<int>(i), handler_);
|
|
}
|
|
|
|
delete enqueue_buffer;
|
|
delete queue;
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace os
|
|
} // namespace bluetooth
|