Skip to content

Commit

Permalink
Add ring_buffer_test + enable building and running within docker
Browse files Browse the repository at this point in the history
  • Loading branch information
Samahu committed Jul 19, 2023
1 parent 0421b1a commit 22c46ff
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ add_library(nodelets_os
target_link_libraries(nodelets_os ouster_ros ${catkin_LIBRARIES})
add_dependencies(nodelets_os ${PROJECT_NAME}_gencpp)

# ==== Test ====
if(CATKIN_ENABLE_TESTING)
catkin_add_gtest(${PROJECT_NAME}_test tests/ring_buffer_test.cpp)
target_link_libraries(${PROJECT_NAME}_test ${catkin_LIBRARIES})
endif()

# ==== Install ====
install(
TARGETS
Expand Down
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ RUN set -xe \
FROM build-env

ENV CXXFLAGS="-Werror -Wno-deprecated-declarations"
RUN /opt/ros/$ROS_DISTRO/env.sh catkin_make -DCMAKE_BUILD_TYPE=Release \
&& /opt/ros/$ROS_DISTRO/env.sh catkin_make install
RUN /opt/ros/$ROS_DISTRO/env.sh catkin_make \
-DCMAKE_BUILD_TYPE=Release --make-args tests \
&& /opt/ros/$ROS_DISTRO/env.sh catkin_make install

RUN source /opt/ros/$ROS_DISTRO/setup.bash && rosrun ouster_ros ouster_ros_test

# Entrypoint for running Ouster ros:
#
Expand Down
181 changes: 181 additions & 0 deletions tests/ring_buffer_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#include <thread>
#include <cstring>
#include <random>
#include <gtest/gtest.h>
#include "../src/thread_safe_ring_buffer.h"

using namespace std::chrono_literals;

class ThreadSafeRingBufferTest : public ::testing::Test {
protected:
static const int ITEM_SIZE = 4; // predefined size for all items used in
static const int ITEM_COUNT = 3; // number of item the buffer could hold

void SetUp() override {
buffer = std::make_unique<ThreadSafeRingBuffer>(ITEM_SIZE, ITEM_COUNT);
}

void TearDown() override {
buffer.reset();
}

std::string rand_str(int size) {
const std::string characters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dist(0, characters.size() - 1);

std::string result;
for (int i = 0; i < size; ++i) {
result += characters[dist(gen)];
}
return result;
}

std::vector<std::string> rand_vector_str(int vec_size, int str_size) {
std::vector<std::string> output(vec_size);
for (auto i = 0; i < vec_size; ++i)
output[i] = rand_str(str_size);
return output;
}

std::vector<std::string> known_vector_str(int vec_size, const std::string& known) {
std::vector<std::string> output(vec_size);
for (auto i = 0; i < vec_size; ++i)
output[i] = known;
return output;
}

std::unique_ptr<ThreadSafeRingBuffer> buffer;
};

TEST_F(ThreadSafeRingBufferTest, ReadWriteToBufferSimple) {

assert (ITEM_COUNT > 1 && "or this test can't run");

const int TOTAL_ITEMS = 10; // total items to process
const std::vector<std::string> source = rand_vector_str(TOTAL_ITEMS, ITEM_SIZE);
std::vector<std::string> target = known_vector_str(TOTAL_ITEMS, "0000");

EXPECT_TRUE(buffer->empty());
EXPECT_FALSE(buffer->full());

for (int i = 0; i < ITEM_COUNT; ++i) {
buffer->write([i, &source](uint8_t* buffer) {
std::memcpy(buffer, &source[i][0], ITEM_SIZE);
});
}

EXPECT_FALSE(buffer->empty());
EXPECT_TRUE(buffer->full());

// remove one item
buffer->read([&target](uint8_t* buffer){
std::memcpy(&target[0][0], buffer, ITEM_SIZE);
});

EXPECT_FALSE(buffer->empty());
EXPECT_FALSE(buffer->full());

for (int i = 1; i < ITEM_COUNT; ++i) {
buffer->read([i, &target](uint8_t* buffer){
std::memcpy(&target[i][0], buffer, ITEM_SIZE);
});
}

EXPECT_TRUE(buffer->empty());
EXPECT_FALSE(buffer->full());

for (int i = 0; i < ITEM_COUNT; ++i) {
std::cout << "source " << source[i] << ", target " << target[i] << std::endl;
EXPECT_EQ(target[i], source[i]);
}
}

TEST_F(ThreadSafeRingBufferTest, ReadWriteToBuffer) {

const int TOTAL_ITEMS = 10; // total items to process
const std::vector<std::string> source = rand_vector_str(TOTAL_ITEMS, ITEM_SIZE);
std::vector<std::string> target = known_vector_str(TOTAL_ITEMS, "0000");

EXPECT_TRUE(buffer->empty());
EXPECT_FALSE(buffer->full());

std::thread producer([this, &source]() {
for (int i = 0; i < TOTAL_ITEMS; ++i) {
buffer->write([i, &source](uint8_t* buffer){
std::memcpy(buffer, &source[i][0], ITEM_SIZE);
});
}
});

std::thread consumer([this, &target]() {
for (int i = 0; i < TOTAL_ITEMS; ++i) {
buffer->read([i, &target](uint8_t* buffer){
std::memcpy(&target[i][0], buffer, ITEM_SIZE);
});
}
});

producer.join();
consumer.join();

for (int i = 0; i < TOTAL_ITEMS; ++i) {
std::cout << "source " << source[i] << ", target " << target[i] << std::endl;
EXPECT_EQ(target[i], source[i]);
}
}

TEST_F(ThreadSafeRingBufferTest, ReadWriteToBufferWithOverwrite) {

const int TOTAL_ITEMS = 10; // total items to process
const std::vector<std::string> source = rand_vector_str(TOTAL_ITEMS, ITEM_SIZE);
std::vector<std::string> target = known_vector_str(TOTAL_ITEMS, "0000");

EXPECT_TRUE(buffer->empty());
EXPECT_FALSE(buffer->full());

std::thread producer([this, &source]() {
for (int i = 0; i < TOTAL_ITEMS; ++i) {
buffer->write_overwrite([i, &source](uint8_t* buffer){
std::memcpy(buffer, &source[i][0], ITEM_SIZE);
});
}
});

// wait for 1 second before starting the consumer thread
// allowing sufficient time for the producer thread to be
// completely done
std::this_thread::sleep_for(1s);
std::thread consumer([this, &target]() {
for (int i = 0; i < TOTAL_ITEMS; ++i) {
buffer->read_timeout([i, &target](uint8_t* buffer){
if (buffer != nullptr)
std::memcpy(&target[i][0], buffer, ITEM_SIZE);
}, 1s);
}
});

producer.join();
consumer.join();

// Since our buffer can host only up to ITEM_COUNT simultanously only the
// last ITEM_COUNT items would have remained in the buffer by the time
// the consumer started processing.
for (int i = 0; i < ITEM_COUNT; ++i) {
std::cout << "source " << source[i] << ", target " << target[i] << std::endl;
EXPECT_EQ(target[i], source[TOTAL_ITEMS-ITEM_COUNT+i]);
}

for (int i = ITEM_COUNT; i < TOTAL_ITEMS; ++i) {
std::cout << "source " << source[i] << ", target " << target[i] << std::endl;
EXPECT_EQ(target[i], "0000");
}
}

int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

0 comments on commit 22c46ff

Please sign in to comment.