// Copyright 2022 The Pigweed Authors // // 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 // // https://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. // // These tests were forked from // https://cs.opensource.google/abseil/abseil-cpp/+/main:absl/algorithm/algorithm_test.cc;drc=38b704384cd2f17590b3922b97744be0b43622c9 // However they were modified to work with containers available in Pigweed // without dynamic allocations, i.e. no std::vector, std::map, std::list, etc. #include "pw_containers/algorithm.h" #include #include #include "gtest/gtest.h" #include "pw_containers/intrusive_list.h" #include "pw_containers/vector.h" #include "pw_span/span.h" namespace { class TestItem : public pw::IntrusiveList::Item { public: TestItem() : number_(0) {} TestItem(int number) : number_(number) {} // Add equality comparison to ensure comparisons are done by identity rather // than equality for the remove function. bool operator==(const TestItem& other) const { return number_ == other.number_; } private: int number_; }; // Most of these tests just check that the code compiles, not that it // does the right thing. That's fine since the functions just forward // to the STL implementation. class NonMutatingTest : public testing::Test { protected: std::array span_array_ = {1, 2, 3}; pw::span span_{NonMutatingTest::span_array_}; pw::Vector vector_ = {1, 2, 3}; int array_[3] = {1, 2, 3}; }; struct AccumulateCalls { void operator()(int value) { calls.push_back(value); } pw::Vector calls; }; bool Predicate(int value) { return value < 3; } bool BinPredicate(int v1, int v2) { return v1 < v2; } bool Equals(int v1, int v2) { return v1 == v2; } } // namespace TEST_F(NonMutatingTest, AllOf) { const pw::Vector& v = vector_; EXPECT_FALSE(pw::containers::AllOf(v, [](int x) { return x > 1; })); EXPECT_TRUE(pw::containers::AllOf(v, [](int x) { return x > 0; })); } TEST_F(NonMutatingTest, AnyOf) { const pw::Vector& v = vector_; EXPECT_TRUE(pw::containers::AnyOf(v, [](int x) { return x > 2; })); EXPECT_FALSE(pw::containers::AnyOf(v, [](int x) { return x > 5; })); } TEST_F(NonMutatingTest, NoneOf) { const pw::Vector& v = vector_; EXPECT_FALSE(pw::containers::NoneOf(v, [](int x) { return x > 2; })); EXPECT_TRUE(pw::containers::NoneOf(v, [](int x) { return x > 5; })); } TEST_F(NonMutatingTest, ForEach) { AccumulateCalls c = pw::containers::ForEach(span_, AccumulateCalls()); std::sort(c.calls.begin(), c.calls.end()); EXPECT_EQ(vector_, c.calls); } TEST_F(NonMutatingTest, FindReturnsCorrectType) { auto it = pw::containers::Find(span_, 3); EXPECT_EQ(3, *it); pw::containers::Find(vector_, 3); } TEST_F(NonMutatingTest, FindIf) { pw::containers::FindIf(span_, Predicate); } TEST_F(NonMutatingTest, FindIfNot) { pw::containers::FindIfNot(span_, Predicate); } TEST_F(NonMutatingTest, FindEnd) { pw::containers::FindEnd(array_, vector_); pw::containers::FindEnd(vector_, array_); } TEST_F(NonMutatingTest, FindEndWithPredicate) { pw::containers::FindEnd(array_, vector_, BinPredicate); pw::containers::FindEnd(vector_, array_, BinPredicate); } TEST_F(NonMutatingTest, FindFirstOf) { pw::containers::FindFirstOf(span_, array_); pw::containers::FindFirstOf(array_, span_); } TEST_F(NonMutatingTest, FindFirstOfWithPredicate) { pw::containers::FindFirstOf(span_, array_, BinPredicate); pw::containers::FindFirstOf(array_, span_, BinPredicate); } TEST_F(NonMutatingTest, AdjacentFind) { pw::containers::AdjacentFind(array_); } TEST_F(NonMutatingTest, AdjacentFindWithPredicate) { pw::containers::AdjacentFind(array_, BinPredicate); } TEST_F(NonMutatingTest, Count) { EXPECT_EQ(1, pw::containers::Count(span_, 3)); } TEST_F(NonMutatingTest, CountIf) { EXPECT_EQ(2, pw::containers::CountIf(span_, Predicate)); } TEST_F(NonMutatingTest, Mismatch) { // Testing necessary as pw::containers::Mismatch executes logic. { auto result = pw::containers::Mismatch(span_, vector_); EXPECT_EQ(result.first, span_.end()); EXPECT_EQ(result.second, vector_.end()); } { auto result = pw::containers::Mismatch(vector_, span_); EXPECT_EQ(result.first, vector_.end()); EXPECT_EQ(result.second, span_.end()); } vector_.back() = 5; { auto result = pw::containers::Mismatch(span_, vector_); EXPECT_EQ(result.first, std::prev(span_.end())); EXPECT_EQ(result.second, std::prev(vector_.end())); } { auto result = pw::containers::Mismatch(vector_, span_); EXPECT_EQ(result.first, std::prev(vector_.end())); EXPECT_EQ(result.second, std::prev(span_.end())); } vector_.pop_back(); { auto result = pw::containers::Mismatch(span_, vector_); EXPECT_EQ(result.first, std::prev(span_.end())); EXPECT_EQ(result.second, vector_.end()); } { auto result = pw::containers::Mismatch(vector_, span_); EXPECT_EQ(result.first, vector_.end()); EXPECT_EQ(result.second, std::prev(span_.end())); } { struct NoNotEquals { constexpr bool operator==(NoNotEquals) const { return true; } constexpr bool operator!=(NoNotEquals) const = delete; }; pw::Vector first; std::array second; // Check this still compiles. pw::containers::Mismatch(first, second); } } TEST_F(NonMutatingTest, MismatchWithPredicate) { // Testing necessary as pw::containers::Mismatch executes logic. { auto result = pw::containers::Mismatch(span_, vector_, BinPredicate); EXPECT_EQ(result.first, span_.begin()); EXPECT_EQ(result.second, vector_.begin()); } { auto result = pw::containers::Mismatch(vector_, span_, BinPredicate); EXPECT_EQ(result.first, vector_.begin()); EXPECT_EQ(result.second, span_.begin()); } vector_.front() = 0; { auto result = pw::containers::Mismatch(span_, vector_, BinPredicate); EXPECT_EQ(result.first, span_.begin()); EXPECT_EQ(result.second, vector_.begin()); } { auto result = pw::containers::Mismatch(vector_, span_, BinPredicate); EXPECT_EQ(result.first, std::next(vector_.begin())); EXPECT_EQ(result.second, std::next(span_.begin())); } vector_.clear(); { auto result = pw::containers::Mismatch(span_, vector_, BinPredicate); EXPECT_EQ(result.first, span_.begin()); EXPECT_EQ(result.second, vector_.end()); } { auto result = pw::containers::Mismatch(vector_, span_, BinPredicate); EXPECT_EQ(result.first, vector_.end()); EXPECT_EQ(result.second, span_.begin()); } } TEST_F(NonMutatingTest, Equal) { EXPECT_TRUE(pw::containers::Equal(vector_, span_)); EXPECT_TRUE(pw::containers::Equal(span_, vector_)); EXPECT_TRUE(pw::containers::Equal(span_, array_)); EXPECT_TRUE(pw::containers::Equal(array_, vector_)); // Test that behavior appropriately differs from that of equal(). pw::Vector vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(pw::containers::Equal(vector_plus, span_)); EXPECT_FALSE(pw::containers::Equal(span_, vector_plus)); EXPECT_FALSE(pw::containers::Equal(array_, vector_plus)); } TEST_F(NonMutatingTest, EqualWithPredicate) { EXPECT_TRUE(pw::containers::Equal(vector_, span_, Equals)); EXPECT_TRUE(pw::containers::Equal(span_, vector_, Equals)); EXPECT_TRUE(pw::containers::Equal(array_, span_, Equals)); EXPECT_TRUE(pw::containers::Equal(vector_, array_, Equals)); // Test that behavior appropriately differs from that of equal(). pw::Vector vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(pw::containers::Equal(vector_plus, span_, Equals)); EXPECT_FALSE(pw::containers::Equal(span_, vector_plus, Equals)); EXPECT_FALSE(pw::containers::Equal(vector_plus, array_, Equals)); } TEST_F(NonMutatingTest, IsPermutation) { auto vector_permut_ = vector_; std::next_permutation(vector_permut_.begin(), vector_permut_.end()); EXPECT_TRUE(pw::containers::IsPermutation(vector_permut_, span_)); EXPECT_TRUE(pw::containers::IsPermutation(span_, vector_permut_)); // Test that behavior appropriately differs from that of is_permutation(). pw::Vector vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(pw::containers::IsPermutation(vector_plus, span_)); EXPECT_FALSE(pw::containers::IsPermutation(span_, vector_plus)); } TEST_F(NonMutatingTest, IsPermutationWithPredicate) { auto vector_permut_ = vector_; std::next_permutation(vector_permut_.begin(), vector_permut_.end()); EXPECT_TRUE(pw::containers::IsPermutation(vector_permut_, span_, Equals)); EXPECT_TRUE(pw::containers::IsPermutation(span_, vector_permut_, Equals)); // Test that behavior appropriately differs from that of is_permutation(). pw::Vector vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(pw::containers::IsPermutation(vector_plus, span_, Equals)); EXPECT_FALSE(pw::containers::IsPermutation(span_, vector_plus, Equals)); } TEST_F(NonMutatingTest, Search) { pw::containers::Search(span_, vector_); pw::containers::Search(vector_, span_); pw::containers::Search(array_, span_); } TEST_F(NonMutatingTest, SearchWithPredicate) { pw::containers::Search(span_, vector_, BinPredicate); pw::containers::Search(vector_, span_, BinPredicate); } TEST_F(NonMutatingTest, SearchN) { pw::containers::SearchN(span_, 3, 1); } TEST_F(NonMutatingTest, SearchNWithPredicate) { pw::containers::SearchN(span_, 3, 1, BinPredicate); }