blob: d361b65cab17c804c52189af574b668acdbd9219 [file] [log] [blame]
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "array.h"
#include "debug.h"
#include <string>
#include <list>
#include <kj/compat/gtest.h>
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());
}
TEST(Array, MapRawArray) {
uint foo[4] = {1, 2, 3, 4};
Array<uint> bar = KJ_MAP(i, foo) -> uint { return i * i; };
ASSERT_EQ(4, bar.size());
EXPECT_EQ(1, bar[0]);
EXPECT_EQ(4, bar[1]);
EXPECT_EQ(9, bar[2]);
EXPECT_EQ(16, bar[3]);
}
TEST(Array, ReleaseAsBytesOrChars) {
{
Array<char> chars = kj::heapArray<char>("foo", 3);
Array<byte> bytes = chars.releaseAsBytes();
EXPECT_TRUE(chars == nullptr);
ASSERT_EQ(3, bytes.size());
EXPECT_EQ('f', bytes[0]);
EXPECT_EQ('o', bytes[1]);
EXPECT_EQ('o', bytes[2]);
chars = bytes.releaseAsChars();
EXPECT_TRUE(bytes == nullptr);
ASSERT_EQ(3, chars.size());
EXPECT_EQ('f', chars[0]);
EXPECT_EQ('o', chars[1]);
EXPECT_EQ('o', chars[2]);
}
{
Array<const char> chars = kj::heapArray<char>("foo", 3);
Array<const byte> bytes = chars.releaseAsBytes();
EXPECT_TRUE(chars == nullptr);
ASSERT_EQ(3, bytes.size());
EXPECT_EQ('f', bytes[0]);
EXPECT_EQ('o', bytes[1]);
EXPECT_EQ('o', bytes[2]);
chars = bytes.releaseAsChars();
EXPECT_TRUE(bytes == nullptr);
ASSERT_EQ(3, chars.size());
EXPECT_EQ('f', chars[0]);
EXPECT_EQ('o', chars[1]);
EXPECT_EQ('o', chars[2]);
}
}
#if __cplusplus > 201402L
KJ_TEST("kj::arr()") {
kj::Array<kj::String> array = kj::arr(kj::str("foo"), kj::str(123));
KJ_EXPECT(array == kj::ArrayPtr<const kj::StringPtr>({"foo", "123"}));
}
struct ImmovableInt {
ImmovableInt(int i): i(i) {}
KJ_DISALLOW_COPY(ImmovableInt);
int i;
};
KJ_TEST("kj::arrOf()") {
kj::Array<ImmovableInt> array = kj::arrOf<ImmovableInt>(123, 456, 789);
KJ_ASSERT(array.size() == 3);
KJ_EXPECT(array[0].i == 123);
KJ_EXPECT(array[1].i == 456);
KJ_EXPECT(array[2].i == 789);
}
#endif
struct DestructionOrderRecorder {
DestructionOrderRecorder(uint& counter, uint& recordTo)
: counter(counter), recordTo(recordTo) {}
~DestructionOrderRecorder() {
recordTo = ++counter;
}
uint& counter;
uint& recordTo;
};
TEST(Array, Attach) {
uint counter = 0;
uint destroyed1 = 0;
uint destroyed2 = 0;
uint destroyed3 = 0;
auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1);
auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2);
auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3);
auto builder = kj::heapArrayBuilder<Own<DestructionOrderRecorder>>(1);
builder.add(kj::mv(obj1));
auto arr = builder.finish();
auto ptr = arr.begin();
Array<Own<DestructionOrderRecorder>> combined = arr.attach(kj::mv(obj2), kj::mv(obj3));
KJ_EXPECT(combined.begin() == ptr);
KJ_EXPECT(obj1.get() == nullptr);
KJ_EXPECT(obj2.get() == nullptr);
KJ_EXPECT(obj3.get() == nullptr);
KJ_EXPECT(destroyed1 == 0);
KJ_EXPECT(destroyed2 == 0);
KJ_EXPECT(destroyed3 == 0);
combined = nullptr;
KJ_EXPECT(destroyed1 == 1, destroyed1);
KJ_EXPECT(destroyed2 == 2, destroyed2);
KJ_EXPECT(destroyed3 == 3, destroyed3);
}
TEST(Array, AttachNested) {
uint counter = 0;
uint destroyed1 = 0;
uint destroyed2 = 0;
uint destroyed3 = 0;
auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1);
auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2);
auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3);
auto builder = kj::heapArrayBuilder<Own<DestructionOrderRecorder>>(1);
builder.add(kj::mv(obj1));
auto arr = builder.finish();
auto ptr = arr.begin();
Array<Own<DestructionOrderRecorder>> combined = arr.attach(kj::mv(obj2)).attach(kj::mv(obj3));
KJ_EXPECT(combined.begin() == ptr);
KJ_EXPECT(combined.size() == 1);
KJ_EXPECT(obj1.get() == nullptr);
KJ_EXPECT(obj2.get() == nullptr);
KJ_EXPECT(obj3.get() == nullptr);
KJ_EXPECT(destroyed1 == 0);
KJ_EXPECT(destroyed2 == 0);
KJ_EXPECT(destroyed3 == 0);
combined = nullptr;
KJ_EXPECT(destroyed1 == 1, destroyed1);
KJ_EXPECT(destroyed2 == 2, destroyed2);
KJ_EXPECT(destroyed3 == 3, destroyed3);
}
TEST(Array, AttachFromArrayPtr) {
uint counter = 0;
uint destroyed1 = 0;
uint destroyed2 = 0;
uint destroyed3 = 0;
auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1);
auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2);
auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3);
auto builder = kj::heapArrayBuilder<Own<DestructionOrderRecorder>>(1);
builder.add(kj::mv(obj1));
auto arr = builder.finish();
auto ptr = arr.begin();
Array<Own<DestructionOrderRecorder>> combined =
arr.asPtr().attach(kj::mv(obj2)).attach(kj::mv(obj3));
KJ_EXPECT(arr != nullptr);
KJ_EXPECT(combined.begin() == ptr);
KJ_EXPECT(obj1.get() == nullptr);
KJ_EXPECT(obj2.get() == nullptr);
KJ_EXPECT(obj3.get() == nullptr);
KJ_EXPECT(destroyed1 == 0);
KJ_EXPECT(destroyed2 == 0);
KJ_EXPECT(destroyed3 == 0);
combined = nullptr;
KJ_EXPECT(destroyed2 == 1, destroyed2);
KJ_EXPECT(destroyed3 == 2, destroyed3);
arr = nullptr;
KJ_EXPECT(destroyed1 == 3, destroyed1);
}
} // namespace
} // namespace kj