diff --git a/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java b/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java index ffb9178cc3..f38f292a93 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java @@ -150,7 +150,9 @@ public class LinearFilter { double retVal = 0.0; // Rotate the inputs - m_inputs.addFirst(input); + if (m_inputGains.length > 0) { + m_inputs.addFirst(input); + } // Calculate the new value for (int i = 0; i < m_inputGains.length; i++) { @@ -161,7 +163,9 @@ public class LinearFilter { } // Rotate the outputs - m_outputs.addFirst(retVal); + if (m_outputGains.length > 0) { + m_outputs.addFirst(retVal); + } return retVal; } diff --git a/wpimath/src/main/native/include/frc/LinearFilter.h b/wpimath/src/main/native/include/frc/LinearFilter.h index 449fadf196..51b6a7f266 100644 --- a/wpimath/src/main/native/include/frc/LinearFilter.h +++ b/wpimath/src/main/native/include/frc/LinearFilter.h @@ -4,9 +4,10 @@ #pragma once -#include +#include #include #include +#include #include #include @@ -81,6 +82,13 @@ class LinearFilter { m_outputs(fbGains.size()), m_inputGains(ffGains), m_outputGains(fbGains) { + for (size_t i = 0; i < ffGains.size(); ++i) { + m_inputs.emplace_front(0.0); + } + for (size_t i = 0; i < fbGains.size(); ++i) { + m_outputs.emplace_front(0.0); + } + static int instances = 0; instances++; wpi::math::MathSharedStore::ReportUsage( @@ -148,7 +156,9 @@ class LinearFilter { * slower */ static LinearFilter MovingAverage(int taps) { - assert(taps > 0); + if (taps <= 0) { + throw std::runtime_error("Number of taps must be greater than zero."); + } std::vector gains(taps, 1.0 / taps); return LinearFilter(gains, {}); @@ -158,8 +168,8 @@ class LinearFilter { * Reset the filter state. */ void Reset() { - m_inputs.reset(); - m_outputs.reset(); + std::fill(m_inputs.begin(), m_inputs.end(), T{0.0}); + std::fill(m_outputs.begin(), m_outputs.end(), T{0.0}); } /** @@ -170,21 +180,25 @@ class LinearFilter { * @return The filtered value at this step */ T Calculate(T input) { - T retVal = T(0.0); + T retVal{0.0}; // Rotate the inputs - m_inputs.push_front(input); + if (m_inputGains.size() > 0) { + m_inputs.push_front(input); + } // Calculate the new value - for (size_t i = 0; i < m_inputGains.size(); i++) { + for (size_t i = 0; i < m_inputGains.size(); ++i) { retVal += m_inputs[i] * m_inputGains[i]; } - for (size_t i = 0; i < m_outputGains.size(); i++) { + for (size_t i = 0; i < m_outputGains.size(); ++i) { retVal -= m_outputs[i] * m_outputGains[i]; } // Rotate the outputs - m_outputs.push_front(retVal); + if (m_outputGains.size() > 0) { + m_outputs.push_front(retVal); + } return retVal; } diff --git a/wpimath/src/test/native/cpp/LinearFilterNoiseTest.cpp b/wpimath/src/test/native/cpp/LinearFilterNoiseTest.cpp index 8d0e613640..2dc55b5825 100644 --- a/wpimath/src/test/native/cpp/LinearFilterNoiseTest.cpp +++ b/wpimath/src/test/native/cpp/LinearFilterNoiseTest.cpp @@ -5,7 +5,6 @@ #include "frc/LinearFilter.h" // NOLINT(build/include_order) #include -#include #include #include @@ -14,26 +13,12 @@ #include "units/time.h" // Filter constants -static constexpr units::second_t kFilterStep = 0.005_s; -static constexpr units::second_t kFilterTime = 2.0_s; +static constexpr auto kFilterStep = 5_ms; +static constexpr auto kFilterTime = 2_s; static constexpr double kSinglePoleIIRTimeConstant = 0.015915; static constexpr int32_t kMovAvgTaps = 6; -enum LinearFilterNoiseTestType { TEST_SINGLE_POLE_IIR, TEST_MOVAVG }; - -std::ostream& operator<<(std::ostream& os, - const LinearFilterNoiseTestType& type) { - switch (type) { - case TEST_SINGLE_POLE_IIR: - os << "LinearFilter SinglePoleIIR"; - break; - case TEST_MOVAVG: - os << "LinearFilter MovingAverage"; - break; - } - - return os; -} +enum LinearFilterNoiseTestType { kTestSinglePoleIIR, kTestMovAvg }; static double GetData(double t) { return 100.0 * std::sin(2.0 * wpi::numbers::pi * t); @@ -42,24 +27,17 @@ static double GetData(double t) { class LinearFilterNoiseTest : public testing::TestWithParam { protected: - std::unique_ptr> m_filter; - - void SetUp() override { + frc::LinearFilter m_filter = [=] { switch (GetParam()) { - case TEST_SINGLE_POLE_IIR: { - m_filter = std::make_unique>( - frc::LinearFilter::SinglePoleIIR(kSinglePoleIIRTimeConstant, - kFilterStep)); + case kTestSinglePoleIIR: + return frc::LinearFilter::SinglePoleIIR( + kSinglePoleIIRTimeConstant, kFilterStep); break; - } - - case TEST_MOVAVG: { - m_filter = std::make_unique>( - frc::LinearFilter::MovingAverage(kMovAvgTaps)); + default: + return frc::LinearFilter::MovingAverage(kMovAvgTaps); break; - } } - } + }(); }; /** @@ -76,7 +54,7 @@ TEST_P(LinearFilterNoiseTest, NoiseReduce) { for (auto t = 0_s; t < kFilterTime; t += kFilterStep) { double theory = GetData(t.to()); double noise = distr(gen); - filterError += std::abs(m_filter->Calculate(theory + noise) - theory); + filterError += std::abs(m_filter.Calculate(theory + noise) - theory); noiseGenError += std::abs(noise - theory); } @@ -88,4 +66,4 @@ TEST_P(LinearFilterNoiseTest, NoiseReduce) { } INSTANTIATE_TEST_SUITE_P(Test, LinearFilterNoiseTest, - testing::Values(TEST_SINGLE_POLE_IIR, TEST_MOVAVG)); + testing::Values(kTestSinglePoleIIR, kTestMovAvg)); diff --git a/wpimath/src/test/native/cpp/LinearFilterOutputTest.cpp b/wpimath/src/test/native/cpp/LinearFilterOutputTest.cpp index b69d1e5bfc..3ce53b0eb5 100644 --- a/wpimath/src/test/native/cpp/LinearFilterOutputTest.cpp +++ b/wpimath/src/test/native/cpp/LinearFilterOutputTest.cpp @@ -15,8 +15,8 @@ #include "units/time.h" // Filter constants -static constexpr units::second_t kFilterStep = 0.005_s; -static constexpr units::second_t kFilterTime = 2.0_s; +static constexpr auto kFilterStep = 5_ms; +static constexpr auto kFilterTime = 2_s; static constexpr double kSinglePoleIIRTimeConstant = 0.015915; static constexpr double kSinglePoleIIRExpectedOutput = -3.2172003; static constexpr double kHighPassTimeConstant = 0.006631; @@ -25,32 +25,12 @@ static constexpr int32_t kMovAvgTaps = 6; static constexpr double kMovAvgExpectedOutput = -10.191644; enum LinearFilterOutputTestType { - TEST_SINGLE_POLE_IIR, - TEST_HIGH_PASS, - TEST_MOVAVG, - TEST_PULSE + kTestSinglePoleIIR, + kTestHighPass, + kTestMovAvg, + kTestPulse }; -std::ostream& operator<<(std::ostream& os, - const LinearFilterOutputTestType& type) { - switch (type) { - case TEST_SINGLE_POLE_IIR: - os << "LinearFilter SinglePoleIIR"; - break; - case TEST_HIGH_PASS: - os << "LinearFilter HighPass"; - break; - case TEST_MOVAVG: - os << "LinearFilter MovingAverage"; - break; - case TEST_PULSE: - os << "LinearFilter Pulse"; - break; - } - - return os; -} - static double GetData(double t) { return 100.0 * std::sin(2.0 * wpi::numbers::pi * t) + 20.0 * std::cos(50.0 * wpi::numbers::pi * t); @@ -70,41 +50,48 @@ static double GetPulseData(double t) { class LinearFilterOutputTest : public testing::TestWithParam { protected: - std::unique_ptr> m_filter; + frc::LinearFilter m_filter = [=] { + switch (GetParam()) { + case kTestSinglePoleIIR: + return frc::LinearFilter::SinglePoleIIR( + kSinglePoleIIRTimeConstant, kFilterStep); + break; + case kTestHighPass: + return frc::LinearFilter::HighPass(kHighPassTimeConstant, + kFilterStep); + break; + case kTestMovAvg: + return frc::LinearFilter::MovingAverage(kMovAvgTaps); + break; + default: + return frc::LinearFilter::MovingAverage(kMovAvgTaps); + break; + } + }(); std::function m_data; double m_expectedOutput = 0.0; - void SetUp() override { + LinearFilterOutputTest() { switch (GetParam()) { - case TEST_SINGLE_POLE_IIR: { - m_filter = std::make_unique>( - frc::LinearFilter::SinglePoleIIR(kSinglePoleIIRTimeConstant, - kFilterStep)); + case kTestSinglePoleIIR: { m_data = GetData; m_expectedOutput = kSinglePoleIIRExpectedOutput; break; } - case TEST_HIGH_PASS: { - m_filter = std::make_unique>( - frc::LinearFilter::HighPass(kHighPassTimeConstant, - kFilterStep)); + case kTestHighPass: { m_data = GetData; m_expectedOutput = kHighPassExpectedOutput; break; } - case TEST_MOVAVG: { - m_filter = std::make_unique>( - frc::LinearFilter::MovingAverage(kMovAvgTaps)); + case kTestMovAvg: { m_data = GetData; m_expectedOutput = kMovAvgExpectedOutput; break; } - case TEST_PULSE: { - m_filter = std::make_unique>( - frc::LinearFilter::MovingAverage(kMovAvgTaps)); + case kTestPulse: { m_data = GetPulseData; m_expectedOutput = 0.0; break; @@ -119,7 +106,7 @@ class LinearFilterOutputTest TEST_P(LinearFilterOutputTest, Output) { double filterOutput = 0.0; for (auto t = 0_s; t < kFilterTime; t += kFilterStep) { - filterOutput = m_filter->Calculate(m_data(t.to())); + filterOutput = m_filter.Calculate(m_data(t.to())); } RecordProperty("LinearFilterOutput", filterOutput); @@ -129,5 +116,5 @@ TEST_P(LinearFilterOutputTest, Output) { } INSTANTIATE_TEST_SUITE_P(Test, LinearFilterOutputTest, - testing::Values(TEST_SINGLE_POLE_IIR, TEST_HIGH_PASS, - TEST_MOVAVG, TEST_PULSE)); + testing::Values(kTestSinglePoleIIR, kTestHighPass, + kTestMovAvg, kTestPulse)); diff --git a/wpiutil/src/main/native/include/wpi/circular_buffer.h b/wpiutil/src/main/native/include/wpi/circular_buffer.h index 798f61ebfc..ad527b3b8d 100644 --- a/wpiutil/src/main/native/include/wpi/circular_buffer.h +++ b/wpiutil/src/main/native/include/wpi/circular_buffer.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include namespace wpi { @@ -16,44 +17,284 @@ namespace wpi { template class circular_buffer { public: - explicit circular_buffer(size_t size); + explicit circular_buffer(size_t size) : m_data(size, T{}) {} - using value_type = T; - using reference = value_type&; - using const_reference = const value_type&; - using pointer = value_type*; - using size_type = size_t; - using iterator_category = std::forward_iterator_tag; - using difference_type = std::ptrdiff_t; + circular_buffer(const circular_buffer&) = default; + circular_buffer& operator=(const circular_buffer&) = default; + circular_buffer(circular_buffer&&) = default; + circular_buffer& operator=(circular_buffer&&) = default; - size_type size() const; - T& front(); - const T& front() const; - T& back(); - const T& back() const; - void push_front(T value); - void push_back(T value); - T pop_front(); - T pop_back(); + class iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using reference = T&; + + iterator(circular_buffer* buffer, size_t index) + : m_buffer(buffer), m_index(index) {} + + iterator& operator++() { + ++m_index; + return *this; + } + iterator operator++(int) { + iterator retval = *this; + ++(*this); + return retval; + } + bool operator==(const iterator& other) const { + return m_buffer == other.m_buffer && m_index == other.m_index; + } + bool operator!=(const iterator& other) const { return !(*this == other); } + reference operator*() { return (*m_buffer)[m_index]; } + + private: + circular_buffer* m_buffer; + size_t m_index; + }; + + class const_iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_reference = const T&; + + const_iterator(const circular_buffer* buffer, size_t index) + : m_buffer(buffer), m_index(index) {} + + const_iterator& operator++() { + ++m_index; + return *this; + } + const_iterator operator++(int) { + const_iterator retval = *this; + ++(*this); + return retval; + } + bool operator==(const const_iterator& other) const { + return m_buffer == other.m_buffer && m_index == other.m_index; + } + bool operator!=(const const_iterator& other) const { + return !(*this == other); + } + const_reference operator*() const { return (*m_buffer)[m_index]; } + + private: + const circular_buffer* m_buffer; + size_t m_index; + }; + + iterator begin() { return iterator(this, 0); } + iterator end() { return iterator(this, ::wpi::circular_buffer::size()); } + + const_iterator begin() const { return const_iterator(this, 0); } + const_iterator end() const { + return const_iterator(this, ::wpi::circular_buffer::size()); + } + + const_iterator cbegin() const { return const_iterator(this, 0); } + const_iterator cend() const { + return const_iterator(this, ::wpi::circular_buffer::size()); + } + + /** + * Returns number of elements in buffer + */ + size_t size() const { return m_length; } + + /** + * Returns value at front of buffer + */ + T& front() { return (*this)[0]; } + + /** + * Returns value at front of buffer + */ + const T& front() const { return (*this)[0]; } + + /** + * Returns value at back of buffer + * + * If there are no elements in the buffer, calling this function results in + * undefined behavior. + */ + T& back() { return m_data[(m_front + m_length - 1) % m_data.size()]; } + + /** + * Returns value at back of buffer + * + * If there are no elements in the buffer, calling this function results in + * undefined behavior. + */ + const T& back() const { + return m_data[(m_front + m_length - 1) % m_data.size()]; + } + + /** + * Push a new value onto the front of the buffer. + * + * The value at the back is overwritten if the buffer is full. + */ + void push_front(T value) { + if (m_data.size() == 0) { + return; + } + + m_front = ModuloDec(m_front); + + m_data[m_front] = value; + + if (m_length < m_data.size()) { + m_length++; + } + } + + /** + * Push a new value onto the back of the buffer. + * + * The value at the front is overwritten if the buffer is full. + */ + void push_back(T value) { + if (m_data.size() == 0) { + return; + } + + m_data[(m_front + m_length) % m_data.size()] = value; + + if (m_length < m_data.size()) { + m_length++; + } else { + // Increment front if buffer is full to maintain size + m_front = ModuloInc(m_front); + } + } + + /** + * Push a new value onto the front of the buffer that is constructed with the + * provided constructor arguments. + * + * The value at the back is overwritten if the buffer is full. + */ + template + void emplace_front(Args&&... args) { + if (m_data.size() == 0) { + return; + } + + m_front = ModuloDec(m_front); + + m_data[m_front] = T{args...}; + + if (m_length < m_data.size()) { + m_length++; + } + } + + /** + * Push a new value onto the back of the buffer that is constructed with the + * provided constructor arguments. + * + * The value at the front is overwritten if the buffer is full. + */ + template + void emplace_back(Args&&... args) { + if (m_data.size() == 0) { + return; + } + + m_data[(m_front + m_length) % m_data.size()] = T{args...}; + + if (m_length < m_data.size()) { + m_length++; + } else { + // Increment front if buffer is full to maintain size + m_front = ModuloInc(m_front); + } + } + + /** + * Pop value at front of buffer. + * + * If there are no elements in the buffer, calling this function results in + * undefined behavior. + */ + T pop_front() { + T& temp = m_data[m_front]; + m_front = ModuloInc(m_front); + m_length--; + return temp; + } + + /** + * Pop value at back of buffer. + * + * If there are no elements in the buffer, calling this function results in + * undefined behavior. + */ + T pop_back() { + m_length--; + return m_data[(m_front + m_length) % m_data.size()]; + } + + /** + * Resizes internal buffer to given size. + */ void resize(size_t size); - void reset(); - T& operator[](size_t index); - const T& operator[](size_t index) const; + /** + * Empties internal buffer. + */ + void reset() { + m_front = 0; + m_length = 0; + } + + /** + * @return Element at index starting from front of buffer. + */ + T& operator[](size_t index) { + return m_data[(m_front + index) % m_data.size()]; + } + + /** + * @return Element at index starting from front of buffer. + */ + const T& operator[](size_t index) const { + return m_data[(m_front + index) % m_data.size()]; + } private: std::vector m_data; - T zero_val{0}; - // Index of element at front of buffer size_t m_front = 0; // Number of elements used in buffer size_t m_length = 0; - size_t ModuloInc(size_t index); - size_t ModuloDec(size_t index); + /** + * Increment an index modulo the length of the buffer. + * + * @return The result of the modulo operation. + */ + size_t ModuloInc(size_t index) { return (index + 1) % m_data.size(); } + + /** + * Decrement an index modulo the length of the buffer. + * + * @return The result of the modulo operation. + */ + size_t ModuloDec(size_t index) { + if (index == 0) { + return m_data.size() - 1; + } else { + return index - 1; + } + } }; } // namespace wpi diff --git a/wpiutil/src/main/native/include/wpi/circular_buffer.inc b/wpiutil/src/main/native/include/wpi/circular_buffer.inc index 14667fc38d..038af50d82 100644 --- a/wpiutil/src/main/native/include/wpi/circular_buffer.inc +++ b/wpiutil/src/main/native/include/wpi/circular_buffer.inc @@ -4,134 +4,10 @@ #pragma once -#include - #include "wpi/circular_buffer.h" namespace wpi { -template -circular_buffer::circular_buffer(size_t size) : m_data(size, T{}) {} - -/** - * Returns number of elements in buffer - */ -template -typename circular_buffer::size_type circular_buffer::size() const { - return m_length; -} - -/** - * Returns value at front of buffer - */ -template -T& circular_buffer::front() { - return (*this)[0]; -} - -/** - * Returns value at front of buffer - */ -template -const T& circular_buffer::front() const { - return (*this)[0]; -} - -/** - * Returns value at back of buffer - */ -template -T& circular_buffer::back() { - // If there are no elements in the buffer, do nothing - if (m_length == 0) { - return zero_val; - } - - return m_data[(m_front + m_length - 1) % m_data.size()]; -} - -/** - * Returns value at back of buffer - */ -template -const T& circular_buffer::back() const { - // If there are no elements in the buffer, do nothing - if (m_length == 0) { - return zero_val; - } - - return m_data[(m_front + m_length - 1) % m_data.size()]; -} - -/** - * Push new value onto front of the buffer. The value at the back is overwritten - * if the buffer is full. - */ -template -void circular_buffer::push_front(T value) { - if (m_data.size() == 0) { - return; - } - - m_front = ModuloDec(m_front); - - m_data[m_front] = value; - - if (m_length < m_data.size()) { - m_length++; - } -} - -/** - * Push new value onto back of the buffer. The value at the front is overwritten - * if the buffer is full. - */ -template -void circular_buffer::push_back(T value) { - if (m_data.size() == 0) { - return; - } - - m_data[(m_front + m_length) % m_data.size()] = value; - - if (m_length < m_data.size()) { - m_length++; - } else { - // Increment front if buffer is full to maintain size - m_front = ModuloInc(m_front); - } -} - -/** - * Pop value at front of buffer. - */ -template -T circular_buffer::pop_front() { - // If there are no elements in the buffer, do nothing - if (m_length == 0) { - return T{0}; - } - - T& temp = m_data[m_front]; - m_front = ModuloInc(m_front); - m_length--; - return temp; -} - -/** - * Pop value at back of buffer. - */ -template -T circular_buffer::pop_back() { - // If there are no elements in the buffer, do nothing - if (m_length == 0) { - return T{0}; - } - - m_length--; - return m_data[(m_front + m_length) % m_data.size()]; -} - /** * Resizes internal buffer to given size. */ @@ -185,54 +61,4 @@ void circular_buffer::resize(size_t size) { } } -/** - * Sets internal buffer contents to zero. - */ -template -void circular_buffer::reset() { - std::fill(m_data.begin(), m_data.end(), T{0}); - m_front = 0; - m_length = 0; -} - -/** - * @return Element at index starting from front of buffer. - */ -template -T& circular_buffer::operator[](size_t index) { - return m_data[(m_front + index) % m_data.size()]; -} - -/** - * @return Element at index starting from front of buffer. - */ -template -const T& circular_buffer::operator[](size_t index) const { - return m_data[(m_front + index) % m_data.size()]; -} - -/** - * Increment an index modulo the length of the buffer. - * - * @return The result of the modulo operation. - */ -template -size_t circular_buffer::ModuloInc(size_t index) { - return (index + 1) % m_data.size(); -} - -/** - * Decrement an index modulo the length of the buffer. - * - * @return The result of the modulo operation. - */ -template -size_t circular_buffer::ModuloDec(size_t index) { - if (index == 0) { - return m_data.size() - 1; - } else { - return index - 1; - } -} - } // namespace wpi diff --git a/wpiutil/src/main/native/include/wpi/static_circular_buffer.h b/wpiutil/src/main/native/include/wpi/static_circular_buffer.h index 31899a393b..b73ea48713 100644 --- a/wpiutil/src/main/native/include/wpi/static_circular_buffer.h +++ b/wpiutil/src/main/native/include/wpi/static_circular_buffer.h @@ -16,9 +16,7 @@ namespace wpi { template class static_circular_buffer { public: - static_assert(N > 0, "The circular buffer size shouldn't be zero."); - - constexpr static_circular_buffer() = default; + static_assert(N > 0, "Circular buffer size cannot be zero."); class iterator { public: diff --git a/wpiutil/src/test/native/cpp/CircularBufferTest.cpp b/wpiutil/src/test/native/cpp/CircularBufferTest.cpp index 88e582cd2c..d47bf76ff2 100644 --- a/wpiutil/src/test/native/cpp/CircularBufferTest.cpp +++ b/wpiutil/src/test/native/cpp/CircularBufferTest.cpp @@ -25,7 +25,7 @@ TEST(CircularBufferTest, PushFrontTest) { queue.push_front(value); } - for (size_t i = 0; i < pushFrontOut.size(); i++) { + for (size_t i = 0; i < pushFrontOut.size(); ++i) { EXPECT_EQ(pushFrontOut[i], queue[i]); } } @@ -37,7 +37,31 @@ TEST(CircularBufferTest, PushBackTest) { queue.push_back(value); } - for (size_t i = 0; i < pushBackOut.size(); i++) { + for (size_t i = 0; i < pushBackOut.size(); ++i) { + EXPECT_EQ(pushBackOut[i], queue[i]); + } +} + +TEST(CircularBufferTest, EmplaceFrontTest) { + wpi::circular_buffer queue(8); + + for (auto& value : values) { + queue.emplace_front(value); + } + + for (size_t i = 0; i < pushFrontOut.size(); ++i) { + EXPECT_EQ(pushFrontOut[i], queue[i]); + } +} + +TEST(CircularBufferTest, EmplaceBackTest) { + wpi::circular_buffer queue(8); + + for (auto& value : values) { + queue.emplace_back(value); + } + + for (size_t i = 0; i < pushBackOut.size(); ++i) { EXPECT_EQ(pushBackOut[i], queue[i]); } } @@ -88,15 +112,13 @@ TEST(CircularBufferTest, PushPopTest) { TEST(CircularBufferTest, ResetTest) { wpi::circular_buffer queue(5); - for (size_t i = 1; i < 6; i++) { + for (size_t i = 1; i < 6; ++i) { queue.push_back(i); } queue.reset(); - for (size_t i = 0; i < 5; i++) { - EXPECT_EQ(0.0, queue[i]); - } + EXPECT_EQ(queue.size(), size_t{0}); } TEST(CircularBufferTest, ResizeTest) { @@ -204,3 +226,29 @@ TEST(CircularBufferTest, ResizeTest) { EXPECT_EQ(2.0, queue[2]); EXPECT_EQ(3.0, queue[3]); } + +TEST(CircularBufferTest, IteratorTest) { + wpi::circular_buffer queue(3); + + queue.push_back(1.0); + queue.push_back(2.0); + queue.push_back(3.0); + queue.push_back(4.0); // Overwrite 1 with 4 + + // The buffer now contains 2, 3 and 4 + const std::array values = {2.0, 3.0, 4.0}; + + // iterator + int i = 0; + for (auto& elem : queue) { + EXPECT_EQ(values[i], elem); + ++i; + } + + // const_iterator + i = 0; + for (const auto& elem : queue) { + EXPECT_EQ(values[i], elem); + ++i; + } +}