From 48907869650880445af9d86a2588ca2c0a963bf7 Mon Sep 17 00:00:00 2001 From: kpraveenkumar Date: Wed, 29 Apr 2020 11:50:20 +0200 Subject: [PATCH 1/6] Implementation of Circular buffer with parameterised test cases --- .gitignore | 1 + CMakeLists.txt | 13 ++++++ include/circular_buffer.h | 61 ++++++++++++++++++++++++++ test/circular_buffer_test.cpp | 82 +++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/circular_buffer.h create mode 100644 test/circular_buffer_test.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4205d75 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.10) +project(CircularBuffer) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -pthread") + +find_package(GTest REQUIRED) +include_directories(include test ${GTEST_INCLUDE_DIRS}) +add_executable(CircularBuffer test/circular_buffer_test.cpp) +target_link_libraries(CircularBuffer ${GTEST_LIBRARIES} pthread gtest gtest_main) +add_test(CircularBuffer CircularBuffer) +add_custom_target(circular_buffer ALL DEPENDS CircularBuffer COMMAND CircularBuffer) \ No newline at end of file diff --git a/include/circular_buffer.h b/include/circular_buffer.h new file mode 100644 index 0000000..cbc2996 --- /dev/null +++ b/include/circular_buffer.h @@ -0,0 +1,61 @@ +#ifndef CIRCULAR_BUFFER_H +#define CIRCULAR_BUFFER_H + +#include +#include +namespace Circular { +namespace Buffer { + +constexpr auto kMaxBufferSize{50}; +constexpr auto kOverRide{true}; +template +class CircularBuffer { + public: + CircularBuffer() = default; + CircularBuffer(const CircularBuffer&) = delete; + CircularBuffer(CircularBuffer&&) = delete; + CircularBuffer& operator=(CircularBuffer&&) = delete; + CircularBuffer& operator=(const CircularBuffer&) = delete; + constexpr auto Capacity() const { return kMaxBufferSize; } + constexpr auto Empty() const { return (!full_ && (head_ == tail_)); } + constexpr auto Full() const { return full_; } + constexpr auto Size() const { + size_t buffer_size{kMaxBufferSize}; + if (full_) { + buffer_size = kMaxBufferSize + head_ - tail_; + } else { + buffer_size = head_ - tail_; + } + return buffer_size; + } + constexpr void PushBack(const T& data, bool override = kOverRide) { + std::lock_guard lock(circular_buffer_mutex_); + if (full_) { + if (override) { + PutDataFull(data); + } + } else { + PutData(data); + } + full_ = head_ == tail_; + } + constexpr T operator[](const size_t& index) const { return buffer_[index]; } + + private: + constexpr void PutData(const T& data) { + buffer_[head_] = data; + head_ = (head_ + 1) % kMaxBufferSize; + } + constexpr void PutDataFull(const T& data) { + PutData(data); + tail_ = (tail_ + 1) % kMaxBufferSize; + } + T buffer_[kMaxBufferSize]; + std::mutex circular_buffer_mutex_; + size_t head_{0}; + size_t tail_{0}; + bool full_{false}; +}; +} // namespace Buffer +} // namespace Circular +#endif \ No newline at end of file diff --git a/test/circular_buffer_test.cpp b/test/circular_buffer_test.cpp new file mode 100644 index 0000000..6e0b127 --- /dev/null +++ b/test/circular_buffer_test.cpp @@ -0,0 +1,82 @@ +#include "circular_buffer.h" +#include +namespace Circular { +namespace Buffer { +namespace { +class CircularBufferTest : public ::testing::Test { + public: + void SetUp() override {} + void TearDown() override {} + CircularBuffer circular_buffer_; +}; +TEST_F(CircularBufferTest, CapacityTest) { + EXPECT_EQ(kMaxBufferSize, circular_buffer_.Capacity()); +} + +class BufferSizeTest : public ::testing::TestWithParam { + public: + void SetUp() override { + result_size_ = GetParam() % kMaxBufferSize; + for (auto index{0u}; index < result_size_; ++index) { + circular_buffer_.PushBack(index); + } + if (0 == result_size_) { + result_empty_ = true; + } + if (kMaxBufferSize == result_size_) { + result_full_ = true; + } + } + void TearDown() override {} + size_t result_size_{0}; + bool result_empty_{false}; + bool result_full_{false}; + CircularBuffer circular_buffer_; +}; + +TEST_P(BufferSizeTest, SizeTest) { + EXPECT_EQ(result_size_, circular_buffer_.Size()); +} +TEST_P(BufferSizeTest, EmptyTest) { + EXPECT_EQ(result_empty_, circular_buffer_.Empty()); +} +TEST_P(BufferSizeTest, FullTest) { + EXPECT_EQ(result_full_, circular_buffer_.Full()); +} +INSTANTIATE_TEST_CASE_P(SizeParam, BufferSizeTest, + ::testing::Values(0, kMaxBufferSize + 1)); + +class BufferDataTest : public ::testing::TestWithParam> { + public: + void SetUp() override { + auto buffer_size = std::get<0>(GetParam()); + auto override_flag = std::get<1>(GetParam()); + for (auto index{0}; index < buffer_size; ++index) { + circular_buffer_.PushBack(index, override_flag); + } + if (override_flag) { + result_start_index_ = buffer_size - kMaxBufferSize; + result_end_index_ = buffer_size; + } else { + result_end_index_ = buffer_size % kMaxBufferSize; + } + } + void TearDown() override {} + size_t result_start_index_{0}; + size_t result_end_index_{0}; + CircularBuffer circular_buffer_; +}; + +TEST_P(BufferDataTest, VerifyBuffer) { + for (auto index{result_start_index_}; index < result_end_index_; ++index) { + EXPECT_EQ(index, circular_buffer_[(index) % kMaxBufferSize]); + } +} +INSTANTIATE_TEST_CASE_P(OverrideDiscardParam, BufferDataTest, + ::testing::Combine(::testing::Values(0, kMaxBufferSize + + 1), + ::testing::Bool())); + +} // namespace +} // namespace Buffer +} // namespace Circular \ No newline at end of file From 6068c57b233089c376ac4ba9b3fcdd6253ad4774 Mon Sep 17 00:00:00 2001 From: kpraveenkumar Date: Wed, 29 Apr 2020 12:00:32 +0200 Subject: [PATCH 2/6] Refactor datatype int to Type using directive --- test/circular_buffer_test.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/circular_buffer_test.cpp b/test/circular_buffer_test.cpp index 6e0b127..ac76ef3 100644 --- a/test/circular_buffer_test.cpp +++ b/test/circular_buffer_test.cpp @@ -3,17 +3,19 @@ namespace Circular { namespace Buffer { namespace { +using Type = std::int64_t; class CircularBufferTest : public ::testing::Test { public: void SetUp() override {} void TearDown() override {} - CircularBuffer circular_buffer_; + CircularBuffer circular_buffer_; }; + TEST_F(CircularBufferTest, CapacityTest) { EXPECT_EQ(kMaxBufferSize, circular_buffer_.Capacity()); } -class BufferSizeTest : public ::testing::TestWithParam { +class BufferSizeTest : public ::testing::TestWithParam { public: void SetUp() override { result_size_ = GetParam() % kMaxBufferSize; @@ -31,7 +33,7 @@ class BufferSizeTest : public ::testing::TestWithParam { size_t result_size_{0}; bool result_empty_{false}; bool result_full_{false}; - CircularBuffer circular_buffer_; + CircularBuffer circular_buffer_; }; TEST_P(BufferSizeTest, SizeTest) { @@ -46,7 +48,7 @@ TEST_P(BufferSizeTest, FullTest) { INSTANTIATE_TEST_CASE_P(SizeParam, BufferSizeTest, ::testing::Values(0, kMaxBufferSize + 1)); -class BufferDataTest : public ::testing::TestWithParam> { +class BufferDataTest : public ::testing::TestWithParam> { public: void SetUp() override { auto buffer_size = std::get<0>(GetParam()); @@ -64,7 +66,7 @@ class BufferDataTest : public ::testing::TestWithParam> { void TearDown() override {} size_t result_start_index_{0}; size_t result_end_index_{0}; - CircularBuffer circular_buffer_; + CircularBuffer circular_buffer_; }; TEST_P(BufferDataTest, VerifyBuffer) { From c4cc70906b7291644ebfd410c8133285937cebb2 Mon Sep 17 00:00:00 2001 From: kpraveenkumar Date: Wed, 29 Apr 2020 12:25:41 +0200 Subject: [PATCH 3/6] Refactor header file inclusion --- test/circular_buffer_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/circular_buffer_test.cpp b/test/circular_buffer_test.cpp index ac76ef3..d0c3611 100644 --- a/test/circular_buffer_test.cpp +++ b/test/circular_buffer_test.cpp @@ -1,4 +1,4 @@ -#include "circular_buffer.h" +#include #include namespace Circular { namespace Buffer { From a8930ac05a53f3834b95b9e95c6fb4a7ed065af3 Mon Sep 17 00:00:00 2001 From: kpraveenkumar Date: Wed, 29 Apr 2020 12:44:09 +0200 Subject: [PATCH 4/6] Update build and run instructions --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 7fb2175..0573bc6 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,31 @@ 3. Generic data type (e.g. uint8_t) 4. Two put(data) overflow behaviors: overwrite and discard data. 2. Write tests showing your circular buffer in action. + +## Install CMake and GTest +### CMake +https://cmake.org/install/ + +### GTest +Linux: +https://github.com/smistad/GTest +Windows: +https://github.com/iat-cener/tonatiuh/wiki/Installing-Google-Test-For-Windows + +Note: Compiler used to compiling GTest and this Circular buffer project needs to be same + +Use clang(clang 6) or gcc(gcc8) which supports C++17 + +## Build +Go to circular_buffer path. Execute the following commands. +1. cmake CMakeLists.txt +2. make + +Circular Buffer is built and run after the commands. +CMakeLists.txt has command to run the CircularBuffer Testcases for each build to encourage Test driven development. + +## Run + +In the circular_buffer path, 'CircularBuffer' executable will be created +Execute the following command to run the testcases. +./CircularBuffer \ No newline at end of file From cac9f45893c2678811bb27cbc7052a7d3e5fccda Mon Sep 17 00:00:00 2001 From: kpraveenkumar Date: Wed, 29 Apr 2020 13:21:53 +0200 Subject: [PATCH 5/6] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0573bc6..844e352 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,11 @@ Go to circular_buffer path. Execute the following commands. 1. cmake CMakeLists.txt 2. make -Circular Buffer is built and run after the commands. +CircularBuffer is built and run, an executable 'CircularBuffer' will be created CMakeLists.txt has command to run the CircularBuffer Testcases for each build to encourage Test driven development. ## Run -In the circular_buffer path, 'CircularBuffer' executable will be created +In the circular_buffer path, 'CircularBuffer' executable will be created after successful build Execute the following command to run the testcases. ./CircularBuffer \ No newline at end of file From bc039b9f39215aaf9bd0dcf3b5ecd675bd6bc4bd Mon Sep 17 00:00:00 2001 From: kpraveenkumar Date: Wed, 29 Apr 2020 13:23:36 +0200 Subject: [PATCH 6/6] Update README.md with format changes --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 844e352..33b36c7 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,13 @@ Go to circular_buffer path. Execute the following commands. 1. cmake CMakeLists.txt 2. make -CircularBuffer is built and run, an executable 'CircularBuffer' will be created +CircularBuffer is built and run, an executable 'CircularBuffer' will be created. + CMakeLists.txt has command to run the CircularBuffer Testcases for each build to encourage Test driven development. ## Run -In the circular_buffer path, 'CircularBuffer' executable will be created after successful build +In the circular_buffer path, 'CircularBuffer' executable will be created after successful build. + Execute the following command to run the testcases. ./CircularBuffer \ No newline at end of file