mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 10:42:39 +01:00
ae65e281f3
to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
998 lines
30 KiB
C++
998 lines
30 KiB
C++
//===- llvm/unittest/ADT/SmallVectorTest.cpp ------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// SmallVector unit tests.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "gtest/gtest.h"
|
|
#include <list>
|
|
#include <stdarg.h>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
/// A helper class that counts the total number of constructor and
|
|
/// destructor calls.
|
|
class Constructable {
|
|
private:
|
|
static int numConstructorCalls;
|
|
static int numMoveConstructorCalls;
|
|
static int numCopyConstructorCalls;
|
|
static int numDestructorCalls;
|
|
static int numAssignmentCalls;
|
|
static int numMoveAssignmentCalls;
|
|
static int numCopyAssignmentCalls;
|
|
|
|
bool constructed;
|
|
int value;
|
|
|
|
public:
|
|
Constructable() : constructed(true), value(0) {
|
|
++numConstructorCalls;
|
|
}
|
|
|
|
Constructable(int val) : constructed(true), value(val) {
|
|
++numConstructorCalls;
|
|
}
|
|
|
|
Constructable(const Constructable & src) : constructed(true) {
|
|
value = src.value;
|
|
++numConstructorCalls;
|
|
++numCopyConstructorCalls;
|
|
}
|
|
|
|
Constructable(Constructable && src) : constructed(true) {
|
|
value = src.value;
|
|
++numConstructorCalls;
|
|
++numMoveConstructorCalls;
|
|
}
|
|
|
|
~Constructable() {
|
|
EXPECT_TRUE(constructed);
|
|
++numDestructorCalls;
|
|
constructed = false;
|
|
}
|
|
|
|
Constructable & operator=(const Constructable & src) {
|
|
EXPECT_TRUE(constructed);
|
|
value = src.value;
|
|
++numAssignmentCalls;
|
|
++numCopyAssignmentCalls;
|
|
return *this;
|
|
}
|
|
|
|
Constructable & operator=(Constructable && src) {
|
|
EXPECT_TRUE(constructed);
|
|
value = src.value;
|
|
++numAssignmentCalls;
|
|
++numMoveAssignmentCalls;
|
|
return *this;
|
|
}
|
|
|
|
int getValue() const {
|
|
return abs(value);
|
|
}
|
|
|
|
static void reset() {
|
|
numConstructorCalls = 0;
|
|
numMoveConstructorCalls = 0;
|
|
numCopyConstructorCalls = 0;
|
|
numDestructorCalls = 0;
|
|
numAssignmentCalls = 0;
|
|
numMoveAssignmentCalls = 0;
|
|
numCopyAssignmentCalls = 0;
|
|
}
|
|
|
|
static int getNumConstructorCalls() {
|
|
return numConstructorCalls;
|
|
}
|
|
|
|
static int getNumMoveConstructorCalls() {
|
|
return numMoveConstructorCalls;
|
|
}
|
|
|
|
static int getNumCopyConstructorCalls() {
|
|
return numCopyConstructorCalls;
|
|
}
|
|
|
|
static int getNumDestructorCalls() {
|
|
return numDestructorCalls;
|
|
}
|
|
|
|
static int getNumAssignmentCalls() {
|
|
return numAssignmentCalls;
|
|
}
|
|
|
|
static int getNumMoveAssignmentCalls() {
|
|
return numMoveAssignmentCalls;
|
|
}
|
|
|
|
static int getNumCopyAssignmentCalls() {
|
|
return numCopyAssignmentCalls;
|
|
}
|
|
|
|
friend bool operator==(const Constructable & c0, const Constructable & c1) {
|
|
return c0.getValue() == c1.getValue();
|
|
}
|
|
|
|
friend bool LLVM_ATTRIBUTE_UNUSED
|
|
operator!=(const Constructable & c0, const Constructable & c1) {
|
|
return c0.getValue() != c1.getValue();
|
|
}
|
|
};
|
|
|
|
int Constructable::numConstructorCalls;
|
|
int Constructable::numCopyConstructorCalls;
|
|
int Constructable::numMoveConstructorCalls;
|
|
int Constructable::numDestructorCalls;
|
|
int Constructable::numAssignmentCalls;
|
|
int Constructable::numCopyAssignmentCalls;
|
|
int Constructable::numMoveAssignmentCalls;
|
|
|
|
struct NonCopyable {
|
|
NonCopyable() {}
|
|
NonCopyable(NonCopyable &&) {}
|
|
NonCopyable &operator=(NonCopyable &&) { return *this; }
|
|
private:
|
|
NonCopyable(const NonCopyable &) = delete;
|
|
NonCopyable &operator=(const NonCopyable &) = delete;
|
|
};
|
|
|
|
LLVM_ATTRIBUTE_USED void CompileTest() {
|
|
SmallVector<NonCopyable, 0> V;
|
|
V.resize(42);
|
|
}
|
|
|
|
class SmallVectorTestBase : public testing::Test {
|
|
protected:
|
|
void SetUp() override { Constructable::reset(); }
|
|
|
|
template <typename VectorT>
|
|
void assertEmpty(VectorT & v) {
|
|
// Size tests
|
|
EXPECT_EQ(0u, v.size());
|
|
EXPECT_TRUE(v.empty());
|
|
|
|
// Iterator tests
|
|
EXPECT_TRUE(v.begin() == v.end());
|
|
}
|
|
|
|
// Assert that v contains the specified values, in order.
|
|
template <typename VectorT>
|
|
void assertValuesInOrder(VectorT & v, size_t size, ...) {
|
|
EXPECT_EQ(size, v.size());
|
|
|
|
va_list ap;
|
|
va_start(ap, size);
|
|
for (size_t i = 0; i < size; ++i) {
|
|
int value = va_arg(ap, int);
|
|
EXPECT_EQ(value, v[i].getValue());
|
|
}
|
|
|
|
va_end(ap);
|
|
}
|
|
|
|
// Generate a sequence of values to initialize the vector.
|
|
template <typename VectorT>
|
|
void makeSequence(VectorT & v, int start, int end) {
|
|
for (int i = start; i <= end; ++i) {
|
|
v.push_back(Constructable(i));
|
|
}
|
|
}
|
|
};
|
|
|
|
// Test fixture class
|
|
template <typename VectorT>
|
|
class SmallVectorTest : public SmallVectorTestBase {
|
|
protected:
|
|
VectorT theVector;
|
|
VectorT otherVector;
|
|
};
|
|
|
|
|
|
typedef ::testing::Types<SmallVector<Constructable, 0>,
|
|
SmallVector<Constructable, 1>,
|
|
SmallVector<Constructable, 2>,
|
|
SmallVector<Constructable, 4>,
|
|
SmallVector<Constructable, 5>
|
|
> SmallVectorTestTypes;
|
|
TYPED_TEST_CASE(SmallVectorTest, SmallVectorTestTypes);
|
|
|
|
// Constructor test.
|
|
TYPED_TEST(SmallVectorTest, ConstructorNonIterTest) {
|
|
SCOPED_TRACE("ConstructorTest");
|
|
this->theVector = SmallVector<Constructable, 2>(2, 2);
|
|
this->assertValuesInOrder(this->theVector, 2u, 2, 2);
|
|
}
|
|
|
|
// Constructor test.
|
|
TYPED_TEST(SmallVectorTest, ConstructorIterTest) {
|
|
SCOPED_TRACE("ConstructorTest");
|
|
int arr[] = {1, 2, 3};
|
|
this->theVector =
|
|
SmallVector<Constructable, 4>(std::begin(arr), std::end(arr));
|
|
this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);
|
|
}
|
|
|
|
// New vector test.
|
|
TYPED_TEST(SmallVectorTest, EmptyVectorTest) {
|
|
SCOPED_TRACE("EmptyVectorTest");
|
|
this->assertEmpty(this->theVector);
|
|
EXPECT_TRUE(this->theVector.rbegin() == this->theVector.rend());
|
|
EXPECT_EQ(0, Constructable::getNumConstructorCalls());
|
|
EXPECT_EQ(0, Constructable::getNumDestructorCalls());
|
|
}
|
|
|
|
// Simple insertions and deletions.
|
|
TYPED_TEST(SmallVectorTest, PushPopTest) {
|
|
SCOPED_TRACE("PushPopTest");
|
|
|
|
// Track whether the vector will potentially have to grow.
|
|
bool RequiresGrowth = this->theVector.capacity() < 3;
|
|
|
|
// Push an element
|
|
this->theVector.push_back(Constructable(1));
|
|
|
|
// Size tests
|
|
this->assertValuesInOrder(this->theVector, 1u, 1);
|
|
EXPECT_FALSE(this->theVector.begin() == this->theVector.end());
|
|
EXPECT_FALSE(this->theVector.empty());
|
|
|
|
// Push another element
|
|
this->theVector.push_back(Constructable(2));
|
|
this->assertValuesInOrder(this->theVector, 2u, 1, 2);
|
|
|
|
// Insert at beginning
|
|
this->theVector.insert(this->theVector.begin(), this->theVector[1]);
|
|
this->assertValuesInOrder(this->theVector, 3u, 2, 1, 2);
|
|
|
|
// Pop one element
|
|
this->theVector.pop_back();
|
|
this->assertValuesInOrder(this->theVector, 2u, 2, 1);
|
|
|
|
// Pop remaining elements
|
|
this->theVector.pop_back();
|
|
this->theVector.pop_back();
|
|
this->assertEmpty(this->theVector);
|
|
|
|
// Check number of constructor calls. Should be 2 for each list element,
|
|
// one for the argument to push_back, one for the argument to insert,
|
|
// and one for the list element itself.
|
|
if (!RequiresGrowth) {
|
|
EXPECT_EQ(5, Constructable::getNumConstructorCalls());
|
|
EXPECT_EQ(5, Constructable::getNumDestructorCalls());
|
|
} else {
|
|
// If we had to grow the vector, these only have a lower bound, but should
|
|
// always be equal.
|
|
EXPECT_LE(5, Constructable::getNumConstructorCalls());
|
|
EXPECT_EQ(Constructable::getNumConstructorCalls(),
|
|
Constructable::getNumDestructorCalls());
|
|
}
|
|
}
|
|
|
|
// Clear test.
|
|
TYPED_TEST(SmallVectorTest, ClearTest) {
|
|
SCOPED_TRACE("ClearTest");
|
|
|
|
this->theVector.reserve(2);
|
|
this->makeSequence(this->theVector, 1, 2);
|
|
this->theVector.clear();
|
|
|
|
this->assertEmpty(this->theVector);
|
|
EXPECT_EQ(4, Constructable::getNumConstructorCalls());
|
|
EXPECT_EQ(4, Constructable::getNumDestructorCalls());
|
|
}
|
|
|
|
// Resize smaller test.
|
|
TYPED_TEST(SmallVectorTest, ResizeShrinkTest) {
|
|
SCOPED_TRACE("ResizeShrinkTest");
|
|
|
|
this->theVector.reserve(3);
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
this->theVector.resize(1);
|
|
|
|
this->assertValuesInOrder(this->theVector, 1u, 1);
|
|
EXPECT_EQ(6, Constructable::getNumConstructorCalls());
|
|
EXPECT_EQ(5, Constructable::getNumDestructorCalls());
|
|
}
|
|
|
|
// Resize bigger test.
|
|
TYPED_TEST(SmallVectorTest, ResizeGrowTest) {
|
|
SCOPED_TRACE("ResizeGrowTest");
|
|
|
|
this->theVector.resize(2);
|
|
|
|
EXPECT_EQ(2, Constructable::getNumConstructorCalls());
|
|
EXPECT_EQ(0, Constructable::getNumDestructorCalls());
|
|
EXPECT_EQ(2u, this->theVector.size());
|
|
}
|
|
|
|
TYPED_TEST(SmallVectorTest, ResizeWithElementsTest) {
|
|
this->theVector.resize(2);
|
|
|
|
Constructable::reset();
|
|
|
|
this->theVector.resize(4);
|
|
|
|
size_t Ctors = Constructable::getNumConstructorCalls();
|
|
EXPECT_TRUE(Ctors == 2 || Ctors == 4);
|
|
size_t MoveCtors = Constructable::getNumMoveConstructorCalls();
|
|
EXPECT_TRUE(MoveCtors == 0 || MoveCtors == 2);
|
|
size_t Dtors = Constructable::getNumDestructorCalls();
|
|
EXPECT_TRUE(Dtors == 0 || Dtors == 2);
|
|
}
|
|
|
|
// Resize with fill value.
|
|
TYPED_TEST(SmallVectorTest, ResizeFillTest) {
|
|
SCOPED_TRACE("ResizeFillTest");
|
|
|
|
this->theVector.resize(3, Constructable(77));
|
|
this->assertValuesInOrder(this->theVector, 3u, 77, 77, 77);
|
|
}
|
|
|
|
// Overflow past fixed size.
|
|
TYPED_TEST(SmallVectorTest, OverflowTest) {
|
|
SCOPED_TRACE("OverflowTest");
|
|
|
|
// Push more elements than the fixed size.
|
|
this->makeSequence(this->theVector, 1, 10);
|
|
|
|
// Test size and values.
|
|
EXPECT_EQ(10u, this->theVector.size());
|
|
for (int i = 0; i < 10; ++i) {
|
|
EXPECT_EQ(i+1, this->theVector[i].getValue());
|
|
}
|
|
|
|
// Now resize back to fixed size.
|
|
this->theVector.resize(1);
|
|
|
|
this->assertValuesInOrder(this->theVector, 1u, 1);
|
|
}
|
|
|
|
// Iteration tests.
|
|
TYPED_TEST(SmallVectorTest, IterationTest) {
|
|
this->makeSequence(this->theVector, 1, 2);
|
|
|
|
// Forward Iteration
|
|
typename TypeParam::iterator it = this->theVector.begin();
|
|
EXPECT_TRUE(*it == this->theVector.front());
|
|
EXPECT_TRUE(*it == this->theVector[0]);
|
|
EXPECT_EQ(1, it->getValue());
|
|
++it;
|
|
EXPECT_TRUE(*it == this->theVector[1]);
|
|
EXPECT_TRUE(*it == this->theVector.back());
|
|
EXPECT_EQ(2, it->getValue());
|
|
++it;
|
|
EXPECT_TRUE(it == this->theVector.end());
|
|
--it;
|
|
EXPECT_TRUE(*it == this->theVector[1]);
|
|
EXPECT_EQ(2, it->getValue());
|
|
--it;
|
|
EXPECT_TRUE(*it == this->theVector[0]);
|
|
EXPECT_EQ(1, it->getValue());
|
|
|
|
// Reverse Iteration
|
|
typename TypeParam::reverse_iterator rit = this->theVector.rbegin();
|
|
EXPECT_TRUE(*rit == this->theVector[1]);
|
|
EXPECT_EQ(2, rit->getValue());
|
|
++rit;
|
|
EXPECT_TRUE(*rit == this->theVector[0]);
|
|
EXPECT_EQ(1, rit->getValue());
|
|
++rit;
|
|
EXPECT_TRUE(rit == this->theVector.rend());
|
|
--rit;
|
|
EXPECT_TRUE(*rit == this->theVector[0]);
|
|
EXPECT_EQ(1, rit->getValue());
|
|
--rit;
|
|
EXPECT_TRUE(*rit == this->theVector[1]);
|
|
EXPECT_EQ(2, rit->getValue());
|
|
}
|
|
|
|
// Swap test.
|
|
TYPED_TEST(SmallVectorTest, SwapTest) {
|
|
SCOPED_TRACE("SwapTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 2);
|
|
std::swap(this->theVector, this->otherVector);
|
|
|
|
this->assertEmpty(this->theVector);
|
|
this->assertValuesInOrder(this->otherVector, 2u, 1, 2);
|
|
}
|
|
|
|
// Append test
|
|
TYPED_TEST(SmallVectorTest, AppendTest) {
|
|
SCOPED_TRACE("AppendTest");
|
|
|
|
this->makeSequence(this->otherVector, 2, 3);
|
|
|
|
this->theVector.push_back(Constructable(1));
|
|
this->theVector.append(this->otherVector.begin(), this->otherVector.end());
|
|
|
|
this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);
|
|
}
|
|
|
|
// Append repeated test
|
|
TYPED_TEST(SmallVectorTest, AppendRepeatedTest) {
|
|
SCOPED_TRACE("AppendRepeatedTest");
|
|
|
|
this->theVector.push_back(Constructable(1));
|
|
this->theVector.append(2, Constructable(77));
|
|
this->assertValuesInOrder(this->theVector, 3u, 1, 77, 77);
|
|
}
|
|
|
|
// Append test
|
|
TYPED_TEST(SmallVectorTest, AppendNonIterTest) {
|
|
SCOPED_TRACE("AppendRepeatedTest");
|
|
|
|
this->theVector.push_back(Constructable(1));
|
|
this->theVector.append(2, 7);
|
|
this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7);
|
|
}
|
|
|
|
struct output_iterator {
|
|
typedef std::output_iterator_tag iterator_category;
|
|
typedef int value_type;
|
|
typedef int difference_type;
|
|
typedef value_type *pointer;
|
|
typedef value_type &reference;
|
|
operator int() { return 2; }
|
|
operator Constructable() { return 7; }
|
|
};
|
|
|
|
TYPED_TEST(SmallVectorTest, AppendRepeatedNonForwardIterator) {
|
|
SCOPED_TRACE("AppendRepeatedTest");
|
|
|
|
this->theVector.push_back(Constructable(1));
|
|
this->theVector.append(output_iterator(), output_iterator());
|
|
this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7);
|
|
}
|
|
|
|
// Assign test
|
|
TYPED_TEST(SmallVectorTest, AssignTest) {
|
|
SCOPED_TRACE("AssignTest");
|
|
|
|
this->theVector.push_back(Constructable(1));
|
|
this->theVector.assign(2, Constructable(77));
|
|
this->assertValuesInOrder(this->theVector, 2u, 77, 77);
|
|
}
|
|
|
|
// Assign test
|
|
TYPED_TEST(SmallVectorTest, AssignRangeTest) {
|
|
SCOPED_TRACE("AssignTest");
|
|
|
|
this->theVector.push_back(Constructable(1));
|
|
int arr[] = {1, 2, 3};
|
|
this->theVector.assign(std::begin(arr), std::end(arr));
|
|
this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3);
|
|
}
|
|
|
|
// Assign test
|
|
TYPED_TEST(SmallVectorTest, AssignNonIterTest) {
|
|
SCOPED_TRACE("AssignTest");
|
|
|
|
this->theVector.push_back(Constructable(1));
|
|
this->theVector.assign(2, 7);
|
|
this->assertValuesInOrder(this->theVector, 2u, 7, 7);
|
|
}
|
|
|
|
// Move-assign test
|
|
TYPED_TEST(SmallVectorTest, MoveAssignTest) {
|
|
SCOPED_TRACE("MoveAssignTest");
|
|
|
|
// Set up our vector with a single element, but enough capacity for 4.
|
|
this->theVector.reserve(4);
|
|
this->theVector.push_back(Constructable(1));
|
|
|
|
// Set up the other vector with 2 elements.
|
|
this->otherVector.push_back(Constructable(2));
|
|
this->otherVector.push_back(Constructable(3));
|
|
|
|
// Move-assign from the other vector.
|
|
this->theVector = std::move(this->otherVector);
|
|
|
|
// Make sure we have the right result.
|
|
this->assertValuesInOrder(this->theVector, 2u, 2, 3);
|
|
|
|
// Make sure the # of constructor/destructor calls line up. There
|
|
// are two live objects after clearing the other vector.
|
|
this->otherVector.clear();
|
|
EXPECT_EQ(Constructable::getNumConstructorCalls()-2,
|
|
Constructable::getNumDestructorCalls());
|
|
|
|
// There shouldn't be any live objects any more.
|
|
this->theVector.clear();
|
|
EXPECT_EQ(Constructable::getNumConstructorCalls(),
|
|
Constructable::getNumDestructorCalls());
|
|
}
|
|
|
|
// Erase a single element
|
|
TYPED_TEST(SmallVectorTest, EraseTest) {
|
|
SCOPED_TRACE("EraseTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
const auto &theConstVector = this->theVector;
|
|
this->theVector.erase(theConstVector.begin());
|
|
this->assertValuesInOrder(this->theVector, 2u, 2, 3);
|
|
}
|
|
|
|
// Erase a range of elements
|
|
TYPED_TEST(SmallVectorTest, EraseRangeTest) {
|
|
SCOPED_TRACE("EraseRangeTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
const auto &theConstVector = this->theVector;
|
|
this->theVector.erase(theConstVector.begin(), theConstVector.begin() + 2);
|
|
this->assertValuesInOrder(this->theVector, 1u, 3);
|
|
}
|
|
|
|
// Insert a single element.
|
|
TYPED_TEST(SmallVectorTest, InsertTest) {
|
|
SCOPED_TRACE("InsertTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
typename TypeParam::iterator I =
|
|
this->theVector.insert(this->theVector.begin() + 1, Constructable(77));
|
|
EXPECT_EQ(this->theVector.begin() + 1, I);
|
|
this->assertValuesInOrder(this->theVector, 4u, 1, 77, 2, 3);
|
|
}
|
|
|
|
// Insert a copy of a single element.
|
|
TYPED_TEST(SmallVectorTest, InsertCopy) {
|
|
SCOPED_TRACE("InsertTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
Constructable C(77);
|
|
typename TypeParam::iterator I =
|
|
this->theVector.insert(this->theVector.begin() + 1, C);
|
|
EXPECT_EQ(this->theVector.begin() + 1, I);
|
|
this->assertValuesInOrder(this->theVector, 4u, 1, 77, 2, 3);
|
|
}
|
|
|
|
// Insert repeated elements.
|
|
TYPED_TEST(SmallVectorTest, InsertRepeatedTest) {
|
|
SCOPED_TRACE("InsertRepeatedTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 4);
|
|
Constructable::reset();
|
|
auto I =
|
|
this->theVector.insert(this->theVector.begin() + 1, 2, Constructable(16));
|
|
// Move construct the top element into newly allocated space, and optionally
|
|
// reallocate the whole buffer, move constructing into it.
|
|
// FIXME: This is inefficient, we shouldn't move things into newly allocated
|
|
// space, then move them up/around, there should only be 2 or 4 move
|
|
// constructions here.
|
|
EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 2 ||
|
|
Constructable::getNumMoveConstructorCalls() == 6);
|
|
// Move assign the next two to shift them up and make a gap.
|
|
EXPECT_EQ(1, Constructable::getNumMoveAssignmentCalls());
|
|
// Copy construct the two new elements from the parameter.
|
|
EXPECT_EQ(2, Constructable::getNumCopyAssignmentCalls());
|
|
// All without any copy construction.
|
|
EXPECT_EQ(0, Constructable::getNumCopyConstructorCalls());
|
|
EXPECT_EQ(this->theVector.begin() + 1, I);
|
|
this->assertValuesInOrder(this->theVector, 6u, 1, 16, 16, 2, 3, 4);
|
|
}
|
|
|
|
TYPED_TEST(SmallVectorTest, InsertRepeatedNonIterTest) {
|
|
SCOPED_TRACE("InsertRepeatedTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 4);
|
|
Constructable::reset();
|
|
auto I = this->theVector.insert(this->theVector.begin() + 1, 2, 7);
|
|
EXPECT_EQ(this->theVector.begin() + 1, I);
|
|
this->assertValuesInOrder(this->theVector, 6u, 1, 7, 7, 2, 3, 4);
|
|
}
|
|
|
|
TYPED_TEST(SmallVectorTest, InsertRepeatedAtEndTest) {
|
|
SCOPED_TRACE("InsertRepeatedTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 4);
|
|
Constructable::reset();
|
|
auto I = this->theVector.insert(this->theVector.end(), 2, Constructable(16));
|
|
// Just copy construct them into newly allocated space
|
|
EXPECT_EQ(2, Constructable::getNumCopyConstructorCalls());
|
|
// Move everything across if reallocation is needed.
|
|
EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 0 ||
|
|
Constructable::getNumMoveConstructorCalls() == 4);
|
|
// Without ever moving or copying anything else.
|
|
EXPECT_EQ(0, Constructable::getNumCopyAssignmentCalls());
|
|
EXPECT_EQ(0, Constructable::getNumMoveAssignmentCalls());
|
|
|
|
EXPECT_EQ(this->theVector.begin() + 4, I);
|
|
this->assertValuesInOrder(this->theVector, 6u, 1, 2, 3, 4, 16, 16);
|
|
}
|
|
|
|
TYPED_TEST(SmallVectorTest, InsertRepeatedEmptyTest) {
|
|
SCOPED_TRACE("InsertRepeatedTest");
|
|
|
|
this->makeSequence(this->theVector, 10, 15);
|
|
|
|
// Empty insert.
|
|
EXPECT_EQ(this->theVector.end(),
|
|
this->theVector.insert(this->theVector.end(),
|
|
0, Constructable(42)));
|
|
EXPECT_EQ(this->theVector.begin() + 1,
|
|
this->theVector.insert(this->theVector.begin() + 1,
|
|
0, Constructable(42)));
|
|
}
|
|
|
|
// Insert range.
|
|
TYPED_TEST(SmallVectorTest, InsertRangeTest) {
|
|
SCOPED_TRACE("InsertRangeTest");
|
|
|
|
Constructable Arr[3] =
|
|
{ Constructable(77), Constructable(77), Constructable(77) };
|
|
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
Constructable::reset();
|
|
auto I = this->theVector.insert(this->theVector.begin() + 1, Arr, Arr + 3);
|
|
// Move construct the top 3 elements into newly allocated space.
|
|
// Possibly move the whole sequence into new space first.
|
|
// FIXME: This is inefficient, we shouldn't move things into newly allocated
|
|
// space, then move them up/around, there should only be 2 or 3 move
|
|
// constructions here.
|
|
EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 2 ||
|
|
Constructable::getNumMoveConstructorCalls() == 5);
|
|
// Copy assign the lower 2 new elements into existing space.
|
|
EXPECT_EQ(2, Constructable::getNumCopyAssignmentCalls());
|
|
// Copy construct the third element into newly allocated space.
|
|
EXPECT_EQ(1, Constructable::getNumCopyConstructorCalls());
|
|
EXPECT_EQ(this->theVector.begin() + 1, I);
|
|
this->assertValuesInOrder(this->theVector, 6u, 1, 77, 77, 77, 2, 3);
|
|
}
|
|
|
|
|
|
TYPED_TEST(SmallVectorTest, InsertRangeAtEndTest) {
|
|
SCOPED_TRACE("InsertRangeTest");
|
|
|
|
Constructable Arr[3] =
|
|
{ Constructable(77), Constructable(77), Constructable(77) };
|
|
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
|
|
// Insert at end.
|
|
Constructable::reset();
|
|
auto I = this->theVector.insert(this->theVector.end(), Arr, Arr+3);
|
|
// Copy construct the 3 elements into new space at the top.
|
|
EXPECT_EQ(3, Constructable::getNumCopyConstructorCalls());
|
|
// Don't copy/move anything else.
|
|
EXPECT_EQ(0, Constructable::getNumCopyAssignmentCalls());
|
|
// Reallocation might occur, causing all elements to be moved into the new
|
|
// buffer.
|
|
EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 0 ||
|
|
Constructable::getNumMoveConstructorCalls() == 3);
|
|
EXPECT_EQ(0, Constructable::getNumMoveAssignmentCalls());
|
|
EXPECT_EQ(this->theVector.begin() + 3, I);
|
|
this->assertValuesInOrder(this->theVector, 6u,
|
|
1, 2, 3, 77, 77, 77);
|
|
}
|
|
|
|
TYPED_TEST(SmallVectorTest, InsertEmptyRangeTest) {
|
|
SCOPED_TRACE("InsertRangeTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
|
|
// Empty insert.
|
|
EXPECT_EQ(this->theVector.end(),
|
|
this->theVector.insert(this->theVector.end(),
|
|
this->theVector.begin(),
|
|
this->theVector.begin()));
|
|
EXPECT_EQ(this->theVector.begin() + 1,
|
|
this->theVector.insert(this->theVector.begin() + 1,
|
|
this->theVector.begin(),
|
|
this->theVector.begin()));
|
|
}
|
|
|
|
// Comparison tests.
|
|
TYPED_TEST(SmallVectorTest, ComparisonTest) {
|
|
SCOPED_TRACE("ComparisonTest");
|
|
|
|
this->makeSequence(this->theVector, 1, 3);
|
|
this->makeSequence(this->otherVector, 1, 3);
|
|
|
|
EXPECT_TRUE(this->theVector == this->otherVector);
|
|
EXPECT_FALSE(this->theVector != this->otherVector);
|
|
|
|
this->otherVector.clear();
|
|
this->makeSequence(this->otherVector, 2, 4);
|
|
|
|
EXPECT_FALSE(this->theVector == this->otherVector);
|
|
EXPECT_TRUE(this->theVector != this->otherVector);
|
|
}
|
|
|
|
// Constant vector tests.
|
|
TYPED_TEST(SmallVectorTest, ConstVectorTest) {
|
|
const TypeParam constVector;
|
|
|
|
EXPECT_EQ(0u, constVector.size());
|
|
EXPECT_TRUE(constVector.empty());
|
|
EXPECT_TRUE(constVector.begin() == constVector.end());
|
|
}
|
|
|
|
// Direct array access.
|
|
TYPED_TEST(SmallVectorTest, DirectVectorTest) {
|
|
EXPECT_EQ(0u, this->theVector.size());
|
|
this->theVector.reserve(4);
|
|
EXPECT_LE(4u, this->theVector.capacity());
|
|
EXPECT_EQ(0, Constructable::getNumConstructorCalls());
|
|
this->theVector.push_back(1);
|
|
this->theVector.push_back(2);
|
|
this->theVector.push_back(3);
|
|
this->theVector.push_back(4);
|
|
EXPECT_EQ(4u, this->theVector.size());
|
|
EXPECT_EQ(8, Constructable::getNumConstructorCalls());
|
|
EXPECT_EQ(1, this->theVector[0].getValue());
|
|
EXPECT_EQ(2, this->theVector[1].getValue());
|
|
EXPECT_EQ(3, this->theVector[2].getValue());
|
|
EXPECT_EQ(4, this->theVector[3].getValue());
|
|
}
|
|
|
|
TYPED_TEST(SmallVectorTest, IteratorTest) {
|
|
std::list<int> L;
|
|
this->theVector.insert(this->theVector.end(), L.begin(), L.end());
|
|
}
|
|
|
|
template <typename InvalidType> class DualSmallVectorsTest;
|
|
|
|
template <typename VectorT1, typename VectorT2>
|
|
class DualSmallVectorsTest<std::pair<VectorT1, VectorT2>> : public SmallVectorTestBase {
|
|
protected:
|
|
VectorT1 theVector;
|
|
VectorT2 otherVector;
|
|
|
|
template <typename T, unsigned N>
|
|
static unsigned NumBuiltinElts(const SmallVector<T, N>&) { return N; }
|
|
};
|
|
|
|
typedef ::testing::Types<
|
|
// Small mode -> Small mode.
|
|
std::pair<SmallVector<Constructable, 4>, SmallVector<Constructable, 4>>,
|
|
// Small mode -> Big mode.
|
|
std::pair<SmallVector<Constructable, 4>, SmallVector<Constructable, 2>>,
|
|
// Big mode -> Small mode.
|
|
std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 4>>,
|
|
// Big mode -> Big mode.
|
|
std::pair<SmallVector<Constructable, 2>, SmallVector<Constructable, 2>>
|
|
> DualSmallVectorTestTypes;
|
|
|
|
TYPED_TEST_CASE(DualSmallVectorsTest, DualSmallVectorTestTypes);
|
|
|
|
TYPED_TEST(DualSmallVectorsTest, MoveAssignment) {
|
|
SCOPED_TRACE("MoveAssignTest-DualVectorTypes");
|
|
|
|
// Set up our vector with four elements.
|
|
for (unsigned I = 0; I < 4; ++I)
|
|
this->otherVector.push_back(Constructable(I));
|
|
|
|
const Constructable *OrigDataPtr = this->otherVector.data();
|
|
|
|
// Move-assign from the other vector.
|
|
this->theVector =
|
|
std::move(static_cast<SmallVectorImpl<Constructable>&>(this->otherVector));
|
|
|
|
// Make sure we have the right result.
|
|
this->assertValuesInOrder(this->theVector, 4u, 0, 1, 2, 3);
|
|
|
|
// Make sure the # of constructor/destructor calls line up. There
|
|
// are two live objects after clearing the other vector.
|
|
this->otherVector.clear();
|
|
EXPECT_EQ(Constructable::getNumConstructorCalls()-4,
|
|
Constructable::getNumDestructorCalls());
|
|
|
|
// If the source vector (otherVector) was in small-mode, assert that we just
|
|
// moved the data pointer over.
|
|
EXPECT_TRUE(this->NumBuiltinElts(this->otherVector) == 4 ||
|
|
this->theVector.data() == OrigDataPtr);
|
|
|
|
// There shouldn't be any live objects any more.
|
|
this->theVector.clear();
|
|
EXPECT_EQ(Constructable::getNumConstructorCalls(),
|
|
Constructable::getNumDestructorCalls());
|
|
|
|
// We shouldn't have copied anything in this whole process.
|
|
EXPECT_EQ(Constructable::getNumCopyConstructorCalls(), 0);
|
|
}
|
|
|
|
struct notassignable {
|
|
int &x;
|
|
notassignable(int &x) : x(x) {}
|
|
};
|
|
|
|
TEST(SmallVectorCustomTest, NoAssignTest) {
|
|
int x = 0;
|
|
SmallVector<notassignable, 2> vec;
|
|
vec.push_back(notassignable(x));
|
|
x = 42;
|
|
EXPECT_EQ(42, vec.pop_back_val().x);
|
|
}
|
|
|
|
struct MovedFrom {
|
|
bool hasValue;
|
|
MovedFrom() : hasValue(true) {
|
|
}
|
|
MovedFrom(MovedFrom&& m) : hasValue(m.hasValue) {
|
|
m.hasValue = false;
|
|
}
|
|
MovedFrom &operator=(MovedFrom&& m) {
|
|
hasValue = m.hasValue;
|
|
m.hasValue = false;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
TEST(SmallVectorTest, MidInsert) {
|
|
SmallVector<MovedFrom, 3> v;
|
|
v.push_back(MovedFrom());
|
|
v.insert(v.begin(), MovedFrom());
|
|
for (MovedFrom &m : v)
|
|
EXPECT_TRUE(m.hasValue);
|
|
}
|
|
|
|
enum EmplaceableArgState {
|
|
EAS_Defaulted,
|
|
EAS_Arg,
|
|
EAS_LValue,
|
|
EAS_RValue,
|
|
EAS_Failure
|
|
};
|
|
template <int I> struct EmplaceableArg {
|
|
EmplaceableArgState State;
|
|
EmplaceableArg() : State(EAS_Defaulted) {}
|
|
EmplaceableArg(EmplaceableArg &&X)
|
|
: State(X.State == EAS_Arg ? EAS_RValue : EAS_Failure) {}
|
|
EmplaceableArg(EmplaceableArg &X)
|
|
: State(X.State == EAS_Arg ? EAS_LValue : EAS_Failure) {}
|
|
|
|
explicit EmplaceableArg(bool) : State(EAS_Arg) {}
|
|
|
|
private:
|
|
EmplaceableArg &operator=(EmplaceableArg &&) = delete;
|
|
EmplaceableArg &operator=(const EmplaceableArg &) = delete;
|
|
};
|
|
|
|
enum EmplaceableState { ES_Emplaced, ES_Moved };
|
|
struct Emplaceable {
|
|
EmplaceableArg<0> A0;
|
|
EmplaceableArg<1> A1;
|
|
EmplaceableArg<2> A2;
|
|
EmplaceableArg<3> A3;
|
|
EmplaceableState State;
|
|
|
|
Emplaceable() : State(ES_Emplaced) {}
|
|
|
|
template <class A0Ty>
|
|
explicit Emplaceable(A0Ty &&A0)
|
|
: A0(std::forward<A0Ty>(A0)), State(ES_Emplaced) {}
|
|
|
|
template <class A0Ty, class A1Ty>
|
|
Emplaceable(A0Ty &&A0, A1Ty &&A1)
|
|
: A0(std::forward<A0Ty>(A0)), A1(std::forward<A1Ty>(A1)),
|
|
State(ES_Emplaced) {}
|
|
|
|
template <class A0Ty, class A1Ty, class A2Ty>
|
|
Emplaceable(A0Ty &&A0, A1Ty &&A1, A2Ty &&A2)
|
|
: A0(std::forward<A0Ty>(A0)), A1(std::forward<A1Ty>(A1)),
|
|
A2(std::forward<A2Ty>(A2)), State(ES_Emplaced) {}
|
|
|
|
template <class A0Ty, class A1Ty, class A2Ty, class A3Ty>
|
|
Emplaceable(A0Ty &&A0, A1Ty &&A1, A2Ty &&A2, A3Ty &&A3)
|
|
: A0(std::forward<A0Ty>(A0)), A1(std::forward<A1Ty>(A1)),
|
|
A2(std::forward<A2Ty>(A2)), A3(std::forward<A3Ty>(A3)),
|
|
State(ES_Emplaced) {}
|
|
|
|
Emplaceable(Emplaceable &&) : State(ES_Moved) {}
|
|
Emplaceable &operator=(Emplaceable &&) {
|
|
State = ES_Moved;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
Emplaceable(const Emplaceable &) = delete;
|
|
Emplaceable &operator=(const Emplaceable &) = delete;
|
|
};
|
|
|
|
TEST(SmallVectorTest, EmplaceBack) {
|
|
EmplaceableArg<0> A0(true);
|
|
EmplaceableArg<1> A1(true);
|
|
EmplaceableArg<2> A2(true);
|
|
EmplaceableArg<3> A3(true);
|
|
{
|
|
SmallVector<Emplaceable, 3> V;
|
|
V.emplace_back();
|
|
EXPECT_TRUE(V.size() == 1);
|
|
EXPECT_TRUE(V.back().State == ES_Emplaced);
|
|
EXPECT_TRUE(V.back().A0.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A1.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A2.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A3.State == EAS_Defaulted);
|
|
}
|
|
{
|
|
SmallVector<Emplaceable, 3> V;
|
|
V.emplace_back(std::move(A0));
|
|
EXPECT_TRUE(V.size() == 1);
|
|
EXPECT_TRUE(V.back().State == ES_Emplaced);
|
|
EXPECT_TRUE(V.back().A0.State == EAS_RValue);
|
|
EXPECT_TRUE(V.back().A1.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A2.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A3.State == EAS_Defaulted);
|
|
}
|
|
{
|
|
SmallVector<Emplaceable, 3> V;
|
|
V.emplace_back(A0);
|
|
EXPECT_TRUE(V.size() == 1);
|
|
EXPECT_TRUE(V.back().State == ES_Emplaced);
|
|
EXPECT_TRUE(V.back().A0.State == EAS_LValue);
|
|
EXPECT_TRUE(V.back().A1.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A2.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A3.State == EAS_Defaulted);
|
|
}
|
|
{
|
|
SmallVector<Emplaceable, 3> V;
|
|
V.emplace_back(A0, A1);
|
|
EXPECT_TRUE(V.size() == 1);
|
|
EXPECT_TRUE(V.back().State == ES_Emplaced);
|
|
EXPECT_TRUE(V.back().A0.State == EAS_LValue);
|
|
EXPECT_TRUE(V.back().A1.State == EAS_LValue);
|
|
EXPECT_TRUE(V.back().A2.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A3.State == EAS_Defaulted);
|
|
}
|
|
{
|
|
SmallVector<Emplaceable, 3> V;
|
|
V.emplace_back(std::move(A0), std::move(A1));
|
|
EXPECT_TRUE(V.size() == 1);
|
|
EXPECT_TRUE(V.back().State == ES_Emplaced);
|
|
EXPECT_TRUE(V.back().A0.State == EAS_RValue);
|
|
EXPECT_TRUE(V.back().A1.State == EAS_RValue);
|
|
EXPECT_TRUE(V.back().A2.State == EAS_Defaulted);
|
|
EXPECT_TRUE(V.back().A3.State == EAS_Defaulted);
|
|
}
|
|
{
|
|
SmallVector<Emplaceable, 3> V;
|
|
V.emplace_back(std::move(A0), A1, std::move(A2), A3);
|
|
EXPECT_TRUE(V.size() == 1);
|
|
EXPECT_TRUE(V.back().State == ES_Emplaced);
|
|
EXPECT_TRUE(V.back().A0.State == EAS_RValue);
|
|
EXPECT_TRUE(V.back().A1.State == EAS_LValue);
|
|
EXPECT_TRUE(V.back().A2.State == EAS_RValue);
|
|
EXPECT_TRUE(V.back().A3.State == EAS_LValue);
|
|
}
|
|
{
|
|
SmallVector<int, 1> V;
|
|
V.emplace_back();
|
|
V.emplace_back(42);
|
|
EXPECT_EQ(2U, V.size());
|
|
EXPECT_EQ(0, V[0]);
|
|
EXPECT_EQ(42, V[1]);
|
|
}
|
|
}
|
|
|
|
TEST(SmallVectorTest, InitializerList) {
|
|
SmallVector<int, 2> V1 = {};
|
|
EXPECT_TRUE(V1.empty());
|
|
V1 = {0, 0};
|
|
EXPECT_TRUE(makeArrayRef(V1).equals({0, 0}));
|
|
V1 = {-1, -1};
|
|
EXPECT_TRUE(makeArrayRef(V1).equals({-1, -1}));
|
|
|
|
SmallVector<int, 2> V2 = {1, 2, 3, 4};
|
|
EXPECT_TRUE(makeArrayRef(V2).equals({1, 2, 3, 4}));
|
|
V2.assign({4});
|
|
EXPECT_TRUE(makeArrayRef(V2).equals({4}));
|
|
V2.append({3, 2});
|
|
EXPECT_TRUE(makeArrayRef(V2).equals({4, 3, 2}));
|
|
V2.insert(V2.begin() + 1, 5);
|
|
EXPECT_TRUE(makeArrayRef(V2).equals({4, 5, 3, 2}));
|
|
}
|
|
|
|
} // end namespace
|