blob: ea85dc2016565b42e367cd225b748eac3b6d9c9e [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/base/containers/SmallVector.h"
#include "android/base/memory/ScopedPtr.h"
#include "android/base/testing/GTestUtils.h"
#include <gtest/gtest.h>
#include <string>
namespace android {
namespace base {
TEST(SmallVector, basic) {
SmallFixedVector<char, 10> sv;
EXPECT_EQ(sv.size(), 0);
EXPECT_TRUE(sv.empty());
EXPECT_EQ(10, sv.capacity());
EXPECT_FALSE(sv.isAllocated());
const int values[] = {1, 2, 3};
sv = SmallFixedVector<char, 10>(values, values + 3);
EXPECT_EQ(3, sv.size());
EXPECT_FALSE(sv.empty());
EXPECT_EQ(10, sv.capacity());
EXPECT_FALSE(sv.isAllocated());
EXPECT_TRUE(RangesMatch(values, sv));
sv.clear();
EXPECT_EQ(0, sv.size());
EXPECT_TRUE(sv.empty());
const char str[] = "this is a long string for insertion";
sv = SmallFixedVector<char, 10>(str);
EXPECT_EQ(sizeof(str), sv.size());
EXPECT_GT(sv.capacity(), 10);
EXPECT_TRUE(sv.isAllocated());
EXPECT_TRUE(RangesMatch(str, sv));
// make sure explicit loops over indices and iterators work
for (size_t i = 0; i != sv.size(); ++i) {
EXPECT_EQ(str[i], sv[i]);
}
const char* c = str;
for (auto i : sv) {
EXPECT_EQ(*c++, i);
}
c = str;
for (auto it = sv.begin(); it != sv.end(); ++it, ++c) {
EXPECT_EQ(*c, *it);
}
c = str;
for (auto it = sv.data(); it != sv.data() + sv.size(); ++it, ++c) {
EXPECT_EQ(*c, *it);
}
}
TEST(SmallVector, ctor) {
{
SmallFixedVector<int, 1> sv;
EXPECT_EQ(sv.size(), 0);
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv(values, values + 3);
EXPECT_TRUE(RangesMatch(values, sv));
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv(values);
EXPECT_TRUE(RangesMatch(values, sv));
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv(values);
EXPECT_TRUE(RangesMatch(values, sv));
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv = {1, 2, 3};
EXPECT_TRUE(RangesMatch(values, sv));
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv1(values);
SmallFixedVector<int, 10> sv2(sv1);
EXPECT_TRUE(RangesMatch(values, sv1));
EXPECT_TRUE(RangesMatch(sv2, sv1));
SmallFixedVector<int, 10> sv3(std::move(sv1));
EXPECT_TRUE(RangesMatch(sv3, values));
// don't check |sv1| - it is not required to be empty.
}
}
TEST(SmallVector, dtor) {
// Count all destructor calls for the elements and make sure they're called
// enough times.
int destructedTimes = 0;
auto deleter = [](int* p) { ++(*p); };
auto item1 = makeCustomScopedPtr(&destructedTimes, deleter);
auto item2 = makeCustomScopedPtr(&destructedTimes, deleter);
auto item3 = makeCustomScopedPtr(&destructedTimes, deleter);
{
SmallFixedVector<decltype(item1), 1> sv;
sv.emplace_back(std::move(item1));
sv.emplace_back(std::move(item2));
// this one is already empty, so it won't add to |destructedTimes|.
sv.emplace_back(std::move(item2));
sv.emplace_back(std::move(item3));
}
EXPECT_EQ(3, destructedTimes);
}
TEST(SmallVector, modifiers) {
SmallFixedVector<unsigned, 5> sv;
EXPECT_EQ(0, sv.size());
EXPECT_EQ(5, sv.capacity());
sv.reserve(4);
EXPECT_EQ(0, sv.size());
EXPECT_EQ(5, sv.capacity());
EXPECT_FALSE(sv.isAllocated());
sv.reserve(6);
EXPECT_EQ(0, sv.size());
EXPECT_EQ(6, sv.capacity());
EXPECT_TRUE(sv.isAllocated());
sv.resize(3);
EXPECT_EQ(3, sv.size());
EXPECT_EQ(6, sv.capacity());
EXPECT_TRUE(sv.isAllocated());
sv.resize(10);
EXPECT_EQ(10, sv.size());
EXPECT_GE(sv.capacity(), 10);
EXPECT_TRUE(sv.isAllocated());
sv.push_back(1);
EXPECT_EQ(11, sv.size());
EXPECT_GE(sv.capacity(), 11);
sv.emplace_back(2);
EXPECT_EQ(12, sv.size());
EXPECT_GE(sv.capacity(), 12);
sv.clear();
EXPECT_EQ(0, sv.size());
EXPECT_GE(sv.capacity(), 12);
// resize_noinit() doesn't really have anything specific we can test
// compared to resize()
sv.resize_noinit(1);
EXPECT_EQ(1, sv.size());
EXPECT_GE(sv.capacity(), 12);
sv.resize_noinit(100);
EXPECT_EQ(100, sv.size());
EXPECT_GE(sv.capacity(), 100);
SmallFixedVector<std::string, 5> strings = {"a", "b", "c"};
strings.emplace_back("d");
strings.push_back("e");
EXPECT_EQ(5, strings.size());
EXPECT_EQ(5, strings.capacity());
EXPECT_FALSE(strings.isAllocated());
strings.push_back(std::string("e"));
EXPECT_EQ(6, strings.size());
EXPECT_GE(strings.capacity(), 6);
EXPECT_TRUE(strings.isAllocated());
}
TEST(SmallVector, useThroughInterface) {
SmallFixedVector<int, 10> sfv = {1, 2, 3};
SmallVector<int>& sv = sfv;
EXPECT_TRUE(RangesMatch(sv, sfv));
EXPECT_EQ(sv.isAllocated(), sfv.isAllocated());
sv.reserve(20);
EXPECT_TRUE(sv.isAllocated());
EXPECT_EQ(sv.isAllocated(), sfv.isAllocated());
// now make sure that deleting through base class cleans up the memory
{
int destructedTimes = 0;
auto deleter = [](int* p) { ++(*p); };
auto item1 = makeCustomScopedPtr(&destructedTimes, deleter);
auto item2 = makeCustomScopedPtr(&destructedTimes, deleter);
SmallVector<decltype(item1)>* sv =
new SmallFixedVector<decltype(item1), 1>();
sv->push_back(std::move(item1));
sv->emplace_back(std::move(item2));
delete sv;
EXPECT_EQ(2, destructedTimes);
}
}
} // namespace android
} // namespace base