| // Copyright 2017-2022 The Verible 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 |
| // |
| // http://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. |
| |
| #include "common/util/container_proxy.h" |
| |
| #include <list> |
| #include <string> |
| #include <vector> |
| |
| #include "common/strings/display_utils.h" |
| #include "common/util/type_traits.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| namespace verible { |
| namespace { |
| |
| using ::testing::ElementsAre; |
| |
| enum class ContainerProxyEvent { |
| kUnknown, |
| kInserted, |
| kBeingRemoved, |
| kBeingReplaced, |
| kWereReplaced, |
| }; |
| |
| template <class Container> |
| struct TestTrace { |
| using value_type = typename Container::value_type; |
| using const_iterator = typename Container::const_iterator; |
| |
| ContainerProxyEvent triggered_method = ContainerProxyEvent::kUnknown; |
| // Snapshot of the container when the method has been called. |
| std::vector<value_type> container_snapshot; |
| |
| int first_index = -1; |
| int last_index = -1; |
| std::vector<value_type> values = {}; |
| |
| // Constructors for creating reference object in tests |
| |
| template <class SnapshotRange, class ValuesRange> |
| TestTrace(ContainerProxyEvent method, const SnapshotRange& snapshot, |
| int first_index, int last_index, const ValuesRange& values) |
| : triggered_method(method), |
| container_snapshot(std::begin(snapshot), std::end(snapshot)), |
| first_index(first_index), |
| last_index(last_index), |
| values(std::begin(values), std::end(values)) {} |
| |
| template <class SnapshotRange> |
| TestTrace(ContainerProxyEvent method, const SnapshotRange& snapshot, |
| int first_index, int last_index, |
| std::initializer_list<value_type> values) |
| : triggered_method(method), |
| container_snapshot(std::begin(snapshot), std::end(snapshot)), |
| first_index(first_index), |
| last_index(last_index), |
| values(values) {} |
| |
| TestTrace(ContainerProxyEvent method, |
| std::initializer_list<value_type> snapshot, int first_index, |
| int last_index, std::initializer_list<value_type> values) |
| : triggered_method(method), |
| container_snapshot(snapshot), |
| first_index(first_index), |
| last_index(last_index), |
| values(values) {} |
| |
| TestTrace(ContainerProxyEvent method, |
| std::initializer_list<value_type> snapshot) |
| : triggered_method(method), container_snapshot(snapshot) {} |
| |
| // Constructors for creating trace objects in a container implementation |
| |
| template <class Range> |
| TestTrace(ContainerProxyEvent method, const Range& container) |
| : triggered_method(method), |
| container_snapshot(container.begin(), container.end()) {} |
| |
| template <class Range> |
| TestTrace(ContainerProxyEvent method, const Range& container, |
| const_iterator first, const_iterator last) |
| : triggered_method(method), |
| container_snapshot(container.begin(), container.end()), |
| first_index(std::distance(container.begin(), first)), |
| last_index(std::distance(container.begin(), last)), |
| values(first, last) {} |
| |
| // Operators |
| |
| bool operator==(const TestTrace& other) const { |
| return triggered_method == other.triggered_method && |
| std::equal(container_snapshot.begin(), container_snapshot.end(), |
| other.container_snapshot.begin()) && |
| first_index == other.first_index && last_index == other.last_index && |
| last_index == other.last_index && |
| std::equal(values.begin(), values.end(), other.values.begin()); |
| } |
| |
| friend std::ostream& operator<<(std::ostream& s, TestTrace obj) { |
| switch (obj.triggered_method) { |
| case ContainerProxyEvent::kUnknown: |
| s << "UNKNOWN"; |
| break; |
| case ContainerProxyEvent::kInserted: |
| s << "inserted"; |
| break; |
| case ContainerProxyEvent::kBeingRemoved: |
| s << "being_removed"; |
| break; |
| case ContainerProxyEvent::kBeingReplaced: |
| s << "being_replaced"; |
| break; |
| case ContainerProxyEvent::kWereReplaced: |
| s << "were_replaced"; |
| break; |
| } |
| s << "("; |
| if (obj.first_index >= 0) { |
| s << "elements = [" << obj.first_index << ", " << obj.last_index << ")"; |
| s << " {" << SequenceFormatter(obj.values) << "}; "; |
| } |
| s << "snapshot = {" << SequenceFormatter(obj.container_snapshot) << "})"; |
| return s; |
| } |
| }; |
| |
| template <class Container> |
| class ContainerProxy |
| : private ContainerProxyBase<ContainerProxy<Container>, Container> { |
| using ThisType = ContainerProxy<Container>; |
| using Base = ContainerProxyBase<ContainerProxy<Container>, Container>; |
| friend Base; |
| |
| public: |
| explicit ContainerProxy(Container& container) : container_(container) {} |
| |
| using typename Base::container_type; |
| |
| using typename Base::value_type; |
| |
| using typename Base::const_reference; |
| using typename Base::reference; |
| |
| using typename Base::const_iterator; |
| using typename Base::iterator; |
| |
| using typename Base::difference_type; |
| using typename Base::size_type; |
| |
| using typename Base::const_reverse_iterator; |
| using typename Base::reverse_iterator; |
| |
| using Base::begin; |
| using Base::cbegin; |
| using Base::cend; |
| using Base::end; |
| |
| using Base::crbegin; |
| using Base::crend; |
| using Base::rbegin; |
| using Base::rend; |
| |
| using Base::back; |
| using Base::front; |
| using Base::operator[]; |
| using Base::at; |
| |
| using Base::empty; |
| using Base::max_size; |
| using Base::size; |
| |
| using Base::emplace_back; |
| using Base::push_back; |
| |
| using Base::emplace_front; |
| using Base::push_front; |
| |
| using Base::emplace; |
| using Base::insert; |
| |
| using Base::clear; |
| using Base::erase; |
| using Base::pop_back; |
| using Base::pop_front; |
| |
| using Base::assign; |
| using Base::operator=; |
| using Base::swap; |
| |
| using Base::capacity; |
| using Base::reserve; |
| using Base::resize; |
| |
| // Tracing data for test purposes. |
| std::vector<TestTrace<ThisType>> trace_data; |
| |
| private: |
| Container& container_; |
| |
| Container& underlying_container() { return container_; } |
| const Container& underlying_container() const { return container_; } |
| |
| void ElementsInserted(const_iterator first, const_iterator last) { |
| trace_data.push_back(TestTrace<ThisType>(ContainerProxyEvent::kInserted, |
| container_, first, last)); |
| } |
| |
| void ElementsBeingRemoved(const_iterator first, const_iterator last) { |
| trace_data.push_back(TestTrace<ThisType>(ContainerProxyEvent::kBeingRemoved, |
| container_, first, last)); |
| } |
| |
| void ElementsBeingReplaced() { |
| trace_data.push_back( |
| TestTrace<ThisType>(ContainerProxyEvent::kBeingReplaced, container_)); |
| } |
| |
| void ElementsWereReplaced() { |
| trace_data.push_back( |
| TestTrace<ThisType>(ContainerProxyEvent::kWereReplaced, container_)); |
| } |
| }; |
| |
| template <class Proxy> |
| class ContainerProxyTest : public ::testing::Test { |
| public: |
| ContainerProxyTest() {} |
| |
| using Container = typename Proxy::container_type; |
| Container container = {"zero", "one", "two"}; |
| Proxy proxy = Proxy(container); |
| }; |
| |
| using BidirectionalContainerProxyTestTypes = ::testing::Types< |
| // vector |
| ContainerProxy<std::vector<std::string>>, |
| const ContainerProxy<std::vector<std::string>>, |
| // list |
| ContainerProxy<std::list<std::string>>, |
| const ContainerProxy<std::list<std::string>> |
| // TODO: deque |
| // TODO: const array |
| >; |
| template <class T> |
| class BidirectionalContainerProxyTest : public ContainerProxyTest<T> {}; |
| TYPED_TEST_SUITE(BidirectionalContainerProxyTest, |
| BidirectionalContainerProxyTestTypes); |
| |
| using MutableBidirectionalContainerProxyTestTypes = ::testing::Types< |
| // vector |
| ContainerProxy<std::vector<std::string>>, |
| // list |
| ContainerProxy<std::list<std::string>> |
| // TODO: deque |
| >; |
| template <class T> |
| class MutableBidirectionalContainerProxyTest : public ContainerProxyTest<T> {}; |
| TYPED_TEST_SUITE(MutableBidirectionalContainerProxyTest, |
| MutableBidirectionalContainerProxyTestTypes); |
| |
| using MutablePrependableContainerProxyTestTypes = ::testing::Types< |
| // list |
| ContainerProxy<std::list<std::string>> |
| // TODO: deque |
| >; |
| template <class T> |
| class MutablePrependableContainerProxyTest : public ContainerProxyTest<T> {}; |
| TYPED_TEST_SUITE(MutablePrependableContainerProxyTest, |
| MutablePrependableContainerProxyTestTypes); |
| |
| using RandomAccessContainerProxyTestTypes = ::testing::Types< |
| // vector |
| ContainerProxy<std::vector<std::string>>, |
| const ContainerProxy<std::vector<std::string>>>; |
| template <class T> |
| class RandomAccessContainerProxyTest : public ContainerProxyTest<T> {}; |
| TYPED_TEST_SUITE(RandomAccessContainerProxyTest, |
| RandomAccessContainerProxyTestTypes); |
| |
| using MutableRandomAccessContainerProxyTestTypes = ::testing::Types< |
| // vector |
| ContainerProxy<std::vector<std::string>>>; |
| template <class T> |
| class MutableRandomAccessContainerProxyTest : public ContainerProxyTest<T> {}; |
| TYPED_TEST_SUITE(MutableRandomAccessContainerProxyTest, |
| MutableRandomAccessContainerProxyTestTypes); |
| |
| // Access |
| |
| TYPED_TEST(BidirectionalContainerProxyTest, Access) { |
| EXPECT_EQ(this->proxy.front(), this->container.front()); |
| EXPECT_EQ(this->proxy.back(), this->container.back()); |
| EXPECT_EQ(&this->proxy.front(), &this->container.front()); |
| EXPECT_EQ(&this->proxy.back(), &this->container.back()); |
| EXPECT_EQ(*this->proxy.begin(), this->container.front()); |
| } |
| |
| TYPED_TEST(RandomAccessContainerProxyTest, Access) { |
| EXPECT_EQ(this->proxy[0], this->container[0]); |
| EXPECT_EQ(this->proxy[1], this->container[1]); |
| EXPECT_EQ(this->proxy[2], this->container[2]); |
| |
| EXPECT_EQ(this->proxy.at(0), this->container[0]); |
| EXPECT_EQ(this->proxy.at(1), this->container[1]); |
| EXPECT_EQ(this->proxy.at(2), this->container[2]); |
| |
| EXPECT_EQ(&this->proxy[0], &this->container[0]); |
| EXPECT_EQ(&this->proxy[1], &this->container[1]); |
| EXPECT_EQ(&this->proxy[2], &this->container[2]); |
| |
| EXPECT_EQ(&this->proxy.at(0), &this->container[0]); |
| EXPECT_EQ(&this->proxy.at(1), &this->container[1]); |
| EXPECT_EQ(&this->proxy.at(2), &this->container[2]); |
| } |
| |
| // Iteration |
| |
| TYPED_TEST(BidirectionalContainerProxyTest, Iteration) { |
| const int initial_size = this->container.size(); |
| |
| { |
| auto iter = this->proxy.begin(); |
| for (int i = 0; i < initial_size; ++i) { |
| EXPECT_EQ(*iter, *std::next(this->container.begin(), i)) << "i = " << i; |
| EXPECT_EQ(&*iter, &*std::next(this->container.begin(), i)) << "i = " << i; |
| ++iter; |
| } |
| EXPECT_EQ(iter, this->proxy.end()); |
| } |
| { |
| auto iter = this->proxy.cbegin(); |
| for (int i = 0; i < initial_size; ++i) { |
| EXPECT_EQ(*iter, *std::next(this->container.begin(), i)) << "i = " << i; |
| EXPECT_EQ(&*iter, &*std::next(this->container.begin(), i)) << "i = " << i; |
| ++iter; |
| } |
| EXPECT_EQ(iter, this->proxy.cend()); |
| } |
| { |
| int i = 0; |
| for (auto& elem : this->proxy) { |
| EXPECT_EQ(elem, *std::next(this->container.begin(), i)) << "i = " << i; |
| EXPECT_EQ(&elem, &*std::next(this->container.begin(), i)) << "i = " << i; |
| ++i; |
| } |
| } |
| } |
| |
| TYPED_TEST(BidirectionalContainerProxyTest, ReverseIteration) { |
| const int initial_size = this->container.size(); |
| |
| { |
| auto iter = this->proxy.rbegin(); |
| for (int i = initial_size - 1; i >= 0; --i) { |
| EXPECT_EQ(*iter, *std::next(this->container.begin(), i)) << "i = " << i; |
| EXPECT_EQ(&*iter, &*std::next(this->container.begin(), i)) << "i = " << i; |
| ++iter; |
| } |
| EXPECT_EQ(iter, this->proxy.rend()); |
| } |
| { |
| auto iter = this->proxy.crbegin(); |
| for (int i = initial_size - 1; i >= 0; --i) { |
| EXPECT_EQ(*iter, *std::next(this->container.begin(), i)) << "i = " << i; |
| EXPECT_EQ(&*iter, &*std::next(this->container.begin(), i)) << "i = " << i; |
| ++iter; |
| } |
| EXPECT_EQ(iter, this->proxy.crend()); |
| } |
| } |
| |
| // Modifiers (inserting) |
| |
| TYPED_TEST(MutableBidirectionalContainerProxyTest, ModifiersPushEmplaceBack) { |
| using Trace = TestTrace<TypeParam>; |
| const int initial_size = this->proxy.size(); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.push_back("three"); |
| EXPECT_EQ(this->proxy.size(), initial_size + 1); |
| EXPECT_EQ(this->proxy.back(), "three"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| initial_size, initial_size + 1, {"three"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| std::string four = std::string("four"); |
| // Make sure the string buffer is dynamically allocated. |
| four.reserve(1000); |
| const auto* four_data = four.data(); |
| this->proxy.push_back(std::move(four)); |
| EXPECT_EQ(this->proxy.size(), initial_size + 2); |
| EXPECT_EQ(this->proxy.back(), "four"); |
| // Verify that the string has been moved and not copied. |
| // TODO(mglb): make sure this is reliable. |
| EXPECT_EQ(this->proxy.back().data(), four_data); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| initial_size + 1, initial_size + 2, {"four"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.emplace_back(5, '5'); |
| EXPECT_EQ(this->proxy.size(), initial_size + 3); |
| EXPECT_EQ(this->proxy.back(), "55555"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| initial_size + 2, initial_size + 3, {"55555"}))); |
| } |
| |
| TYPED_TEST(MutablePrependableContainerProxyTest, ModifiersPushEmplaceFront) { |
| using Trace = TestTrace<TypeParam>; |
| const int initial_size = this->proxy.size(); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.push_front("minus_one"); |
| EXPECT_EQ(this->proxy.size(), initial_size + 1); |
| EXPECT_EQ(this->proxy.front(), "minus_one"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 0, 1, {"minus_one"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| std::string minus_two = std::string("minus_two"); |
| // Make sure the string buffer is dynamically allocated. |
| minus_two.reserve(1000); |
| const auto* minus_two_data = minus_two.data(); |
| this->proxy.push_front(std::move(minus_two)); |
| EXPECT_EQ(this->proxy.size(), initial_size + 2); |
| EXPECT_EQ(this->proxy.front(), "minus_two"); |
| // Verify that the string has been moved and not copied. |
| // TODO(mglb): make sure this is reliable. |
| EXPECT_EQ(this->proxy.front().data(), minus_two_data); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 0, 1, {"minus_two"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.emplace_front(3, '-'); |
| EXPECT_EQ(this->proxy.size(), initial_size + 3); |
| EXPECT_EQ(this->proxy.front(), "---"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 0, 1, {"---"}))); |
| } |
| |
| TYPED_TEST(MutableBidirectionalContainerProxyTest, ModifiersInsertEmplace) { |
| using Trace = TestTrace<TypeParam>; |
| |
| const int initial_size = this->proxy.size(); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.emplace(std::next(this->proxy.begin()), 3, 'a'); |
| EXPECT_EQ(this->proxy.size(), initial_size + 1); |
| EXPECT_EQ(*std::next(this->proxy.begin()), "aaa"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 1, 2, {"aaa"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.insert(this->proxy.begin(), "foo"); |
| EXPECT_EQ(this->proxy.size(), initial_size + 2); |
| EXPECT_EQ(this->proxy.front(), "foo"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 0, 1, {"foo"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.insert(std::next(this->proxy.begin(), 3), {"bbb", "ccc", "ddd"}); |
| EXPECT_EQ(this->proxy.size(), initial_size + 5); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 3), "bbb"); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 4), "ccc"); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 5), "ddd"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 3, 6, {"bbb", "ccc", "ddd"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| static const std::string source[] = {"eee", "fff", "ggg"}; |
| this->proxy.insert(std::next(this->proxy.begin(), 6), std::begin(source), |
| std::end(source)); |
| EXPECT_EQ(this->proxy.size(), initial_size + 8); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 6), "eee"); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 7), "fff"); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 8), "ggg"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 6, 9, {"eee", "fff", "ggg"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| std::string s = std::string("bar"); |
| // Make sure the string buffer is dynamically allocated. |
| s.reserve(1000); |
| const auto* s_data = s.data(); |
| this->proxy.insert(std::next(this->proxy.begin(), 1), std::move(s)); |
| EXPECT_EQ(this->proxy.size(), initial_size + 9); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 1), "bar"); |
| // Verify that the string has been moved and not copied. |
| // TODO(mglb): make sure this is reliable. |
| EXPECT_EQ(std::next(this->proxy.begin(), 1)->data(), s_data); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 1, 2, {"bar"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.insert(std::next(this->proxy.begin(), 2), 3, "baz"); |
| EXPECT_EQ(this->proxy.size(), initial_size + 12); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 2), "baz"); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 3), "baz"); |
| EXPECT_EQ(*std::next(this->proxy.begin(), 4), "baz"); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, this->container, // |
| 2, 5, {"baz", "baz", "baz"}))); |
| |
| // Check whole container, just to be sure. |
| |
| EXPECT_THAT(this->container, ElementsAre("foo", "bar", "baz", "baz", "baz", |
| "zero", "aaa", "bbb", "ccc", "ddd", |
| "eee", "fff", "ggg", "one", "two")); |
| } |
| |
| // Modifiers (removing) |
| |
| TYPED_TEST(MutableBidirectionalContainerProxyTest, ModifiersErase) { |
| using Trace = TestTrace<TypeParam>; |
| using iterator = typename TypeParam::iterator; |
| using const_iterator = typename TypeParam::const_iterator; |
| |
| // Add a few more elements |
| this->container.push_back("three"); |
| this->container.push_back("four"); |
| this->container.push_back("five"); |
| this->container.push_back("six"); |
| this->container.push_back("seven"); |
| this->container.push_back("eight"); |
| |
| { |
| this->proxy.trace_data.clear(); |
| |
| iterator pos = std::next(this->proxy.begin(), 2); // "two" |
| this->proxy.erase(pos); |
| EXPECT_THAT(this->container, ElementsAre("zero", "one", "three", "four", |
| "five", "six", "seven", "eight")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "two", "three", "four", |
| "five", "six", "seven", "eight"}, // |
| 2, 3, {"two"}))); |
| } |
| { |
| this->proxy.trace_data.clear(); |
| |
| const_iterator pos = std::next(this->proxy.begin(), 2); // "three" |
| this->proxy.erase(pos); |
| EXPECT_THAT(this->container, ElementsAre("zero", "one", "four", "five", |
| "six", "seven", "eight")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "three", "four", "five", |
| "six", "seven", "eight"}, // |
| 2, 3, {"three"}))); |
| } |
| { |
| this->proxy.trace_data.clear(); |
| |
| iterator first = std::next(this->proxy.begin(), 2); // "four" |
| iterator last = std::next(this->proxy.begin(), 5); // "seven" |
| this->proxy.erase(first, last); |
| EXPECT_THAT(this->container, ElementsAre("zero", "one", "seven", "eight")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "four", "five", "six", |
| "seven", "eight"}, // |
| 2, 5, {"four", "five", "six"}))); |
| } |
| { |
| this->proxy.trace_data.clear(); |
| |
| const_iterator first = std::next(this->proxy.begin(), 1); // "one" |
| const_iterator last = std::next(this->proxy.begin(), 3); // "eight" |
| this->proxy.erase(first, last); |
| EXPECT_THAT(this->container, ElementsAre("zero", "eight")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "seven", "eight"}, // |
| 1, 3, {"one", "seven"}))); |
| } |
| } |
| |
| TYPED_TEST(MutablePrependableContainerProxyTest, ModifiersPopFront) { |
| using Trace = TestTrace<TypeParam>; |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.pop_front(); |
| EXPECT_THAT(this->container, ElementsAre("one", "two")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "two"}, // |
| 0, 1, {"zero"}))); |
| } |
| |
| TYPED_TEST(MutableRandomAccessContainerProxyTest, ModifiersPopBack) { |
| using Trace = TestTrace<TypeParam>; |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.pop_back(); |
| EXPECT_THAT(this->container, ElementsAre("zero", "one")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "two"}, // |
| 2, 3, {"two"}))); |
| } |
| |
| TYPED_TEST(MutableRandomAccessContainerProxyTest, ModifiersClear) { |
| using Trace = TestTrace<TypeParam>; |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.clear(); |
| EXPECT_TRUE(this->container.empty()); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "two"}, // |
| 0, 3, {"zero", "one", "two"}))); |
| } |
| |
| // Assignment |
| |
| TYPED_TEST(MutableBidirectionalContainerProxyTest, Assign) { |
| using Trace = TestTrace<TypeParam>; |
| |
| static const std::vector<std::string> other_container = {"foo", "bar", "baz"}; |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.assign(other_container.begin(), other_container.end()); |
| EXPECT_THAT(this->container, ElementsAre("foo", "bar", "baz")); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"zero", "one", "two"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"foo", "bar", "baz"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.assign({"x", "y"}); |
| EXPECT_THAT(this->container, ElementsAre("x", "y")); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"foo", "bar", "baz"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"x", "y"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.assign(4, "FOUR"); |
| EXPECT_THAT(this->container, ElementsAre("FOUR", "FOUR", "FOUR", "FOUR")); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingReplaced, {"x", "y"}), |
| Trace(ContainerProxyEvent::kWereReplaced, |
| {"FOUR", "FOUR", "FOUR", "FOUR"}))); |
| } |
| |
| TYPED_TEST(MutableBidirectionalContainerProxyTest, AssignmentOperator) { |
| using Trace = TestTrace<TypeParam>; |
| using Container = typename TypeParam::container_type; |
| |
| Container other_container = {"foo", "bar", "baz"}; |
| |
| this->proxy.trace_data.clear(); |
| |
| auto& proxy_ref_0 = this->proxy = other_container; |
| static_assert(std::is_same_v<decltype(proxy_ref_0), TypeParam&>); |
| EXPECT_THAT(this->container, ElementsAre("foo", "bar", "baz")); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"zero", "one", "two"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"foo", "bar", "baz"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| auto& proxy_ref_1 = this->proxy = {"x", "y"}; |
| static_assert(std::is_same_v<decltype(proxy_ref_1), TypeParam&>); |
| EXPECT_THAT(this->container, ElementsAre("x", "y")); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"foo", "bar", "baz"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"x", "y"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| auto* const foo_ptr = &other_container.front(); |
| auto& proxy_ref_2 = this->proxy = std::move(other_container); |
| static_assert(std::is_same_v<decltype(proxy_ref_2), TypeParam&>); |
| EXPECT_THAT(this->container, ElementsAre("foo", "bar", "baz")); |
| // Verify that the container has been moved and not copied. |
| EXPECT_EQ(&this->container.front(), foo_ptr); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"x", "y"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"foo", "bar", "baz"}))); |
| } |
| |
| TYPED_TEST(MutableBidirectionalContainerProxyTest, Swap) { |
| using Proxy = TypeParam; |
| using Container = typename Proxy::container_type; |
| using Trace = TestTrace<TypeParam>; |
| |
| Container other_container = {"foo", "bar"}; |
| Proxy other_proxy = Proxy(other_container); |
| |
| this->proxy.trace_data.clear(); |
| other_proxy.trace_data.clear(); |
| |
| this->proxy.swap(other_proxy); |
| EXPECT_THAT(this->container, ElementsAre("foo", "bar")); |
| EXPECT_THAT(other_container, ElementsAre("zero", "one", "two")); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"zero", "one", "two"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"foo", "bar"}))); |
| EXPECT_THAT( |
| other_proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"foo", "bar"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"zero", "one", "two"}))); |
| |
| this->proxy.trace_data.clear(); |
| other_proxy.trace_data.clear(); |
| |
| swap(this->proxy, other_proxy); |
| EXPECT_THAT(this->container, ElementsAre("zero", "one", "two")); |
| EXPECT_THAT(other_container, ElementsAre("foo", "bar")); |
| EXPECT_THAT( |
| this->proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"foo", "bar"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"zero", "one", "two"}))); |
| EXPECT_THAT( |
| other_proxy.trace_data, |
| ElementsAre( |
| Trace(ContainerProxyEvent::kBeingReplaced, {"zero", "one", "two"}), |
| Trace(ContainerProxyEvent::kWereReplaced, {"foo", "bar"}))); |
| |
| Container container_without_proxy = {"qux", "quux", "quz", "quuz"}; |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.swap(container_without_proxy); |
| EXPECT_THAT(this->container, ElementsAre("qux", "quux", "quz", "quuz")); |
| EXPECT_THAT(container_without_proxy, ElementsAre("zero", "one", "two")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingReplaced, |
| {"zero", "one", "two"}), |
| Trace(ContainerProxyEvent::kWereReplaced, |
| {"qux", "quux", "quz", "quuz"}))); |
| } |
| |
| // Capacity |
| |
| TYPED_TEST(BidirectionalContainerProxyTest, Capacity) { |
| EXPECT_EQ(this->proxy.size(), this->container.size()); |
| EXPECT_EQ(this->proxy.empty(), this->container.empty()); |
| EXPECT_EQ(this->proxy.max_size(), this->container.max_size()); |
| } |
| |
| TYPED_TEST(RandomAccessContainerProxyTest, Capacity) { |
| EXPECT_EQ(this->proxy.capacity(), this->container.capacity()); |
| } |
| |
| TYPED_TEST(MutableBidirectionalContainerProxyTest, Capacity) { |
| using Trace = TestTrace<TypeParam>; |
| |
| const int initial_size = this->proxy.size(); |
| const int resized_up_size = initial_size + 3; |
| const int resized_down_size = 2; |
| const int resized_down_again_size = 1; |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.resize(resized_up_size); |
| EXPECT_EQ(this->proxy.size(), resized_up_size); |
| EXPECT_THAT(this->container, ElementsAre("zero", "one", "two", "", "", "")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, |
| {"zero", "one", "two", "", "", ""}, // |
| initial_size, resized_up_size, // |
| {"", "", ""}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.resize(resized_down_size); |
| EXPECT_EQ(this->proxy.size(), resized_down_size); |
| EXPECT_THAT(this->container, ElementsAre("zero", "one")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "two", "", "", ""}, // |
| resized_down_size, resized_up_size, // |
| {"two", "", "", ""}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.resize(resized_up_size, "1"); |
| EXPECT_EQ(this->proxy.size(), resized_up_size); |
| EXPECT_THAT(this->container, ElementsAre("zero", "one", "1", "1", "1", "1")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kInserted, |
| {"zero", "one", "1", "1", "1", "1"}, // |
| resized_down_size, resized_up_size, // |
| {"1", "1", "1", "1"}))); |
| |
| this->proxy.trace_data.clear(); |
| |
| this->proxy.resize(resized_down_again_size, "whatever, will not be used"); |
| EXPECT_EQ(this->proxy.size(), resized_down_again_size); |
| EXPECT_THAT(this->container, ElementsAre("zero")); |
| EXPECT_THAT(this->proxy.trace_data, |
| ElementsAre(Trace(ContainerProxyEvent::kBeingRemoved, |
| {"zero", "one", "1", "1", "1", "1"}, // |
| resized_down_again_size, resized_up_size, // |
| {"one", "1", "1", "1", "1"}))); |
| } |
| |
| TYPED_TEST(MutableRandomAccessContainerProxyTest, Capacity) { |
| const int initial_capacity = this->proxy.capacity(); |
| EXPECT_EQ(this->proxy.capacity(), this->container.capacity()); |
| |
| const int new_capacity = initial_capacity + 42; |
| this->proxy.reserve(new_capacity); |
| EXPECT_EQ(this->proxy.capacity(), new_capacity); |
| EXPECT_EQ(this->container.capacity(), new_capacity); |
| |
| const int lower_capacity = 1; |
| this->proxy.reserve(lower_capacity); |
| EXPECT_EQ(this->proxy.capacity(), new_capacity); |
| EXPECT_EQ(this->container.capacity(), new_capacity); |
| } |
| |
| } // namespace |
| } // namespace verible |