blob: 833d2c31ecd3e75fbba82f6bef4093da0d00a060 [file] [log] [blame]
// 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