Add unit test and fixes for TSQueue

This commit is contained in:
Stephen Seo 2019-06-07 11:33:44 +09:00
parent 76567d168e
commit 8548d4f6ed
5 changed files with 169 additions and 27 deletions

View file

@ -26,11 +26,17 @@ target_compile_features(UDPConnection PUBLIC cxx_std_11)
target_link_libraries(UDPConnection PUBLIC pthread) target_link_libraries(UDPConnection PUBLIC pthread)
if(CMAKE_BUILD_TYPE MATCHES "Debug") if(CMAKE_BUILD_TYPE MATCHES "Debug")
set(UDPC_UnitTest_SOURCES
src/test/UDPC_UnitTest.cpp) find_package(GTest QUIET)
add_executable(UnitTest ${UDPC_UnitTest_SOURCES}) if(GTEST_FOUND)
target_link_libraries(UnitTest PUBLIC UDPConnection) set(UDPC_UnitTest_SOURCES
target_include_directories(UnitTest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) src/test/UDPC_UnitTest.cpp
src/test/TestTSQueue.cpp
)
add_executable(UnitTest ${UDPC_UnitTest_SOURCES})
target_link_libraries(UnitTest PUBLIC UDPConnection ${GTEST_BOTH_LIBRARIES})
target_include_directories(UnitTest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
endif()
set(UDPC_NetworkTest_SOURCES set(UDPC_NetworkTest_SOURCES
src/test/UDPC_NetworkTest.cpp) src/test/UDPC_NetworkTest.cpp)

View file

@ -3,17 +3,19 @@
#include <cstring> #include <cstring>
TSQueue::TSQueue(unsigned int elemSize, unsigned int capacity) TSQueue::TSQueue(unsigned int elemSize, unsigned int capacity)
: elemSize(elemSize), capacity(capacity), head(0), tail(0), isEmpty(true), : elemSize(elemSize), head(0), tail(0), isEmpty(true),
spinLock(false) { spinLock(false) {
if (elemSize == 0) { if (elemSize == 0) {
this->elemSize = 1; this->elemSize = 1;
} }
if (capacity == 0) { if (capacity == 0) {
this->capacity = UDPC_TSQUEUE_DEFAULT_CAPACITY * this->elemSize; this->capacityBytes = UDPC_TSQUEUE_DEFAULT_CAPACITY * this->elemSize;
} else {
this->capacityBytes = capacity * this->elemSize;
} }
this->buffer = this->buffer =
std::unique_ptr<unsigned char[]>(new unsigned char[this->capacity]); std::unique_ptr<unsigned char[]>(new unsigned char[this->capacityBytes]);
} }
TSQueue::~TSQueue() {} TSQueue::~TSQueue() {}
@ -27,7 +29,7 @@ bool TSQueue::push(void *data) {
} }
memcpy(buffer.get() + tail, data, elemSize); memcpy(buffer.get() + tail, data, elemSize);
tail = (tail + elemSize) % capacity; tail = (tail + elemSize) % capacityBytes;
isEmpty = false; isEmpty = false;
@ -44,11 +46,7 @@ std::unique_ptr<unsigned char[]> TSQueue::top() {
} }
auto data = std::unique_ptr<unsigned char[]>(new unsigned char[elemSize]); auto data = std::unique_ptr<unsigned char[]>(new unsigned char[elemSize]);
if (tail != 0) { memcpy(data.get(), buffer.get() + head, elemSize);
memcpy(data.get(), buffer.get() + (tail - elemSize), elemSize);
} else {
memcpy(data.get(), buffer.get() + (capacity - elemSize), elemSize);
}
spinLock.store(false); spinLock.store(false);
return data; return data;
} }
@ -61,7 +59,7 @@ bool TSQueue::pop() {
return false; return false;
} }
head += elemSize; head += elemSize;
if (head >= capacity) { if (head >= capacityBytes) {
head = 0; head = 0;
} }
if (head == tail) { if (head == tail) {
@ -89,14 +87,14 @@ void TSQueue::changeCapacity(unsigned int newCapacity) {
while (spinLock.exchange(true) == true) { while (spinLock.exchange(true) == true) {
} }
// repeat of size() to avoid deadlock // repeat of sizeBytes() to avoid deadlock
unsigned int size; unsigned int size;
if (head == tail) { if (head == tail) {
size = capacity; size = capacityBytes;
} else if (head < tail) { } else if (head < tail) {
size = tail - head; size = tail - head;
} else { } else {
size = capacity - head + tail; size = capacityBytes - head + tail;
} }
unsigned int newCap = newCapacity * elemSize; unsigned int newCap = newCapacity * elemSize;
@ -107,15 +105,15 @@ void TSQueue::changeCapacity(unsigned int newCapacity) {
unsigned int tempHead = head; unsigned int tempHead = head;
if (size > newCap) { if (size > newCap) {
unsigned int diff = size - newCap; unsigned int diff = size - newCap;
tempHead = (head + diff) % capacity; tempHead = (head + diff) % capacityBytes;
} }
if (tempHead < tail) { if (tempHead < tail) {
memcpy(newBuffer.get(), buffer.get() + tempHead, tail - tempHead); memcpy(newBuffer.get(), buffer.get() + tempHead, tail - tempHead);
} else { } else {
memcpy(newBuffer.get(), buffer.get() + tempHead, memcpy(newBuffer.get(), buffer.get() + tempHead,
capacity - tempHead); capacityBytes - tempHead);
if (tail != 0) { if (tail != 0) {
memcpy(newBuffer.get() + capacity - tempHead, buffer.get(), memcpy(newBuffer.get() + capacityBytes - tempHead, buffer.get(),
tail); tail);
} }
} }
@ -126,7 +124,7 @@ void TSQueue::changeCapacity(unsigned int newCapacity) {
tail = tail - head; tail = tail - head;
head = 0; head = 0;
} else { } else {
tail = capacity - head + tail; tail = capacityBytes - head + tail;
head = 0; head = 0;
} }
} else { } else {
@ -135,7 +133,7 @@ void TSQueue::changeCapacity(unsigned int newCapacity) {
isEmpty = false; isEmpty = false;
} }
buffer = std::move(newBuffer); buffer = std::move(newBuffer);
capacity = newCap; capacityBytes = newCap;
spinLock.store(false); spinLock.store(false);
} }
@ -151,11 +149,34 @@ unsigned int TSQueue::size() {
unsigned int size; unsigned int size;
if (head == tail) { if (head == tail) {
size = capacity; size = capacityBytes;
} else if (head < tail) { } else if (head < tail) {
size = tail - head; size = tail - head;
} else { } else {
size = capacity - head + tail; size = capacityBytes - head + tail;
}
size /= elemSize;
spinLock.store(false);
return size;
}
unsigned int TSQueue::sizeBytes() {
while (spinLock.exchange(true) == true) {
}
if (isEmpty) {
spinLock.store(false);
return 0;
}
unsigned int size;
if (head == tail) {
size = capacityBytes;
} else if (head < tail) {
size = tail - head;
} else {
size = capacityBytes - head + tail;
} }
spinLock.store(false); spinLock.store(false);

View file

@ -29,12 +29,14 @@ class TSQueue {
private: private:
unsigned int elemSize; unsigned int elemSize;
unsigned int capacity; unsigned int capacityBytes;
unsigned int head; unsigned int head;
unsigned int tail; unsigned int tail;
bool isEmpty; bool isEmpty;
std::unique_ptr<unsigned char[]> buffer; std::unique_ptr<unsigned char[]> buffer;
std::atomic_bool spinLock; std::atomic_bool spinLock;
unsigned int sizeBytes();
}; };
#endif #endif

View file

@ -0,0 +1,109 @@
#include <gtest/gtest.h>
#include "TSQueue.hpp"
TEST(TSQueue, Usage)
{
TSQueue q(sizeof(int), 4);
int temp = 100;
EXPECT_EQ(q.size(), 0);
EXPECT_FALSE(q.pop());
EXPECT_TRUE(q.push(&temp));
EXPECT_EQ(q.size(), 1);
// { 100 }
temp = 200;
EXPECT_TRUE(q.push(&temp));
EXPECT_EQ(q.size(), 2);
auto top = q.top();
EXPECT_EQ(100, *((int*)top.get()));
// { 100, 200 }
temp = 300;
EXPECT_TRUE(q.push(&temp));
EXPECT_EQ(q.size(), 3);
// { 100, 200, 300 }
temp = 400;
EXPECT_TRUE(q.push(&temp));
EXPECT_EQ(q.size(), 4);
// { 100, 200, 300, 400 }
temp = 500;
EXPECT_FALSE(q.push(&temp));
EXPECT_EQ(q.size(), 4);
top = q.top();
EXPECT_EQ(100, *((int*)top.get()));
EXPECT_TRUE(q.pop());
EXPECT_EQ(q.size(), 3);
// { 200, 300, 400 }
top = q.top();
EXPECT_EQ(200, *((int*)top.get()));
temp = 1;
EXPECT_TRUE(q.push(&temp));
EXPECT_EQ(q.size(), 4);
// { 200, 300, 400, 1 }
top = q.top();
EXPECT_EQ(200, *((int*)top.get()));
temp = 2;
EXPECT_FALSE(q.push(&temp));
EXPECT_EQ(q.size(), 4);
q.changeCapacity(8);
EXPECT_EQ(q.size(), 4);
temp = 10;
EXPECT_TRUE(q.push(&temp));
EXPECT_EQ(q.size(), 5);
// { 200, 300, 400, 1, 10 }
top = q.top();
EXPECT_EQ(200, *((int*)top.get()));
EXPECT_TRUE(q.pop());
EXPECT_EQ(q.size(), 4);
// { 300, 400, 1, 10 }
top = q.top();
EXPECT_EQ(300, *((int*)top.get()));
EXPECT_TRUE(q.pop());
EXPECT_EQ(q.size(), 3);
// { 400, 1, 10 }
top = q.top();
EXPECT_EQ(400, *((int*)top.get()));
q.changeCapacity(1);
// { 10 }
EXPECT_EQ(q.size(), 1);
top = q.top();
EXPECT_EQ(10, *((int*)top.get()));
EXPECT_TRUE(q.pop());
// { }
EXPECT_FALSE(q.pop());
EXPECT_EQ(0, q.size());
}

View file

@ -1,2 +1,6 @@
int main() { #include <gtest/gtest.h>
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
} }