blob: 9bd7c7704b35a915e95c1dc1c26dfc1ace281c8e [file] [log] [blame]
// Copyright (c) 2013, Kenton Varda <[email protected]>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "array.h"
#include <gtest/gtest.h>
#include "debug.h"
#include <string>
#include <list>
namespace kj {
namespace {
struct TestObject {
TestObject() {
index = count;
KJ_ASSERT(index != throwAt);
++count;
}
TestObject(const TestObject& other) {
KJ_ASSERT(other.index != throwAt);
index = -1;
copiedCount++;
}
~TestObject() noexcept(false) {
if (index == -1) {
--copiedCount;
} else {
--count;
EXPECT_EQ(index, count);
KJ_ASSERT(count != throwAt);
}
}
int index;
static int count;
static int copiedCount;
static int throwAt;
};
int TestObject::count = 0;
int TestObject::copiedCount = 0;
int TestObject::throwAt = -1;
struct TestNoexceptObject {
TestNoexceptObject() noexcept {
index = count;
++count;
}
TestNoexceptObject(const TestNoexceptObject& other) noexcept {
index = -1;
copiedCount++;
}
~TestNoexceptObject() noexcept {
if (index == -1) {
--copiedCount;
} else {
--count;
EXPECT_EQ(index, count);
}
}
int index;
static int count;
static int copiedCount;
};
int TestNoexceptObject::count = 0;
int TestNoexceptObject::copiedCount = 0;
TEST(Array, TrivialConstructor) {
// char* ptr;
{
Array<char> chars = heapArray<char>(32);
// ptr = chars.begin();
chars[0] = 12;
chars[1] = 34;
}
{
Array<char> chars = heapArray<char>(32);
// TODO(test): The following doesn't work in opt mode -- I guess some allocators zero the
// memory? Is there some other way we can test this? Maybe override malloc()?
// // Somewhat hacky: We can't guarantee that the new array is allocated in the same place, but
// // any reasonable allocator is highly likely to do so. If it does, then we expect that the
// // memory has not been initialized.
// if (chars.begin() == ptr) {
// EXPECT_NE(chars[0], 0);
// EXPECT_NE(chars[1], 0);
// }
}
}
TEST(Array, ComplexConstructor) {
TestObject::count = 0;
TestObject::throwAt = -1;
{
Array<TestObject> array = heapArray<TestObject>(32);
EXPECT_EQ(32, TestObject::count);
}
EXPECT_EQ(0, TestObject::count);
}
#if !KJ_NO_EXCEPTIONS
TEST(Array, ThrowingConstructor) {
TestObject::count = 0;
TestObject::throwAt = 16;
// If a constructor throws, the previous elements should still be destroyed.
EXPECT_ANY_THROW(heapArray<TestObject>(32));
EXPECT_EQ(0, TestObject::count);
}
TEST(Array, ThrowingDestructor) {
TestObject::count = 0;
TestObject::throwAt = -1;
Array<TestObject> array = heapArray<TestObject>(32);
EXPECT_EQ(32, TestObject::count);
// If a destructor throws, all elements should still be destroyed.
TestObject::throwAt = 16;
EXPECT_ANY_THROW(array = nullptr);
EXPECT_EQ(0, TestObject::count);
}
#endif // !KJ_NO_EXCEPTIONS
TEST(Array, AraryBuilder) {
TestObject::count = 0;
TestObject::throwAt = -1;
Array<TestObject> array;
{
ArrayBuilder<TestObject> builder = heapArrayBuilder<TestObject>(32);
for (int i = 0; i < 32; i++) {
EXPECT_EQ(i, TestObject::count);
builder.add();
}
EXPECT_EQ(32, TestObject::count);
array = builder.finish();
EXPECT_EQ(32, TestObject::count);
}
EXPECT_EQ(32, TestObject::count);
array = nullptr;
EXPECT_EQ(0, TestObject::count);
}
TEST(Array, AraryBuilderAddAll) {
{
// Trivial case.
char text[] = "foo";
ArrayBuilder<char> builder = heapArrayBuilder<char>(5);
builder.add('<');
builder.addAll(text, text + 3);
builder.add('>');
auto array = builder.finish();
EXPECT_EQ("<foo>", std::string(array.begin(), array.end()));
}
{
// Trivial case, const.
const char* text = "foo";
ArrayBuilder<char> builder = heapArrayBuilder<char>(5);
builder.add('<');
builder.addAll(text, text + 3);
builder.add('>');
auto array = builder.finish();
EXPECT_EQ("<foo>", std::string(array.begin(), array.end()));
}
{
// Trivial case, non-pointer iterator.
std::list<char> text = {'f', 'o', 'o'};
ArrayBuilder<char> builder = heapArrayBuilder<char>(5);
builder.add('<');
builder.addAll(text);
builder.add('>');
auto array = builder.finish();
EXPECT_EQ("<foo>", std::string(array.begin(), array.end()));
}
{
// Complex case.
std::string strs[] = {"foo", "bar", "baz"};
ArrayBuilder<std::string> builder = heapArrayBuilder<std::string>(5);
builder.add("qux");
builder.addAll(strs, strs + 3);
builder.add("quux");
auto array = builder.finish();
EXPECT_EQ("qux", array[0]);
EXPECT_EQ("foo", array[1]);
EXPECT_EQ("bar", array[2]);
EXPECT_EQ("baz", array[3]);
EXPECT_EQ("quux", array[4]);
}
{
// Complex case, noexcept.
TestNoexceptObject::count = 0;
TestNoexceptObject::copiedCount = 0;
TestNoexceptObject objs[3];
EXPECT_EQ(3, TestNoexceptObject::count);
EXPECT_EQ(0, TestNoexceptObject::copiedCount);
ArrayBuilder<TestNoexceptObject> builder = heapArrayBuilder<TestNoexceptObject>(3);
EXPECT_EQ(3, TestNoexceptObject::count);
EXPECT_EQ(0, TestNoexceptObject::copiedCount);
builder.addAll(objs, objs + 3);
EXPECT_EQ(3, TestNoexceptObject::count);
EXPECT_EQ(3, TestNoexceptObject::copiedCount);
auto array = builder.finish();
EXPECT_EQ(3, TestNoexceptObject::count);
EXPECT_EQ(3, TestNoexceptObject::copiedCount);
}
EXPECT_EQ(0, TestNoexceptObject::count);
EXPECT_EQ(0, TestNoexceptObject::copiedCount);
{
// Complex case, exceptions possible.
TestObject::count = 0;
TestObject::copiedCount = 0;
TestObject::throwAt = -1;
TestObject objs[3];
EXPECT_EQ(3, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount);
ArrayBuilder<TestObject> builder = heapArrayBuilder<TestObject>(3);
EXPECT_EQ(3, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount);
builder.addAll(objs, objs + 3);
EXPECT_EQ(3, TestObject::count);
EXPECT_EQ(3, TestObject::copiedCount);
auto array = builder.finish();
EXPECT_EQ(3, TestObject::count);
EXPECT_EQ(3, TestObject::copiedCount);
}
EXPECT_EQ(0, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount);
#if !KJ_NO_EXCEPTIONS
{
// Complex case, exceptions occur.
TestObject::count = 0;
TestObject::copiedCount = 0;
TestObject::throwAt = -1;
TestObject objs[3];
EXPECT_EQ(3, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount);
TestObject::throwAt = 1;
ArrayBuilder<TestObject> builder = heapArrayBuilder<TestObject>(3);
EXPECT_EQ(3, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount);
EXPECT_ANY_THROW(builder.addAll(objs, objs + 3));
TestObject::throwAt = -1;
EXPECT_EQ(3, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount);
}
EXPECT_EQ(0, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount);
#endif // !KJ_NO_EXCEPTIONS
}
TEST(Array, HeapCopy) {
{
Array<char> copy = heapArray("foo", 3);
EXPECT_EQ(3u, copy.size());
EXPECT_EQ("foo", std::string(copy.begin(), 3));
}
{
Array<char> copy = heapArray(ArrayPtr<const char>("bar", 3));
EXPECT_EQ(3u, copy.size());
EXPECT_EQ("bar", std::string(copy.begin(), 3));
}
{
const char* ptr = "baz";
Array<char> copy = heapArray<char>(ptr, ptr + 3);
EXPECT_EQ(3u, copy.size());
EXPECT_EQ("baz", std::string(copy.begin(), 3));
}
}
TEST(Array, OwnConst) {
ArrayBuilder<int> builder = heapArrayBuilder<int>(2);
int x[2] = {123, 234};
builder.addAll(x, x + 2);
Array<int> i = builder.finish(); //heapArray<int>({123, 234});
ASSERT_EQ(2u, i.size());
EXPECT_EQ(123, i[0]);
EXPECT_EQ(234, i[1]);
Array<const int> ci = mv(i);
ASSERT_EQ(2u, ci.size());
EXPECT_EQ(123, ci[0]);
EXPECT_EQ(234, ci[1]);
Array<const int> ci2 = heapArray<const int>({345, 456});
ASSERT_EQ(2u, ci2.size());
EXPECT_EQ(345, ci2[0]);
EXPECT_EQ(456, ci2[1]);
}
TEST(Array, Map) {
StringPtr foo = "abcd";
Array<char> bar = KJ_MAP(c, foo) -> char { return c + 1; };
EXPECT_STREQ("bcde", str(bar).cStr());
}
} // namespace
} // namespace kj