mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 13:28:11 +00:00

This commit makes it possible to instantiate `Vector<T&>` and use it to store references to `T` in a vector. All non-pointer observers are made to return the reference, and the pointer observers simply yield the underlying pointer. Note that the 'find_*' methods act on the values and not the pointers that are stored in the vector. This commit also makes errors in various vector methods much more readable by directly using requires-clauses on them. And finally, it should be noted that Vector cannot hold temporaries :^)
512 lines
11 KiB
C++
512 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibTest/TestCase.h>
|
|
|
|
#include <AK/NonnullOwnPtrVector.h>
|
|
#include <AK/OwnPtr.h>
|
|
#include <AK/String.h>
|
|
#include <AK/Vector.h>
|
|
|
|
TEST_CASE(construct)
|
|
{
|
|
EXPECT(Vector<int>().is_empty());
|
|
EXPECT(Vector<int>().size() == 0);
|
|
}
|
|
|
|
TEST_CASE(ints)
|
|
{
|
|
Vector<int> ints;
|
|
ints.append(1);
|
|
ints.append(2);
|
|
ints.append(3);
|
|
EXPECT_EQ(ints.size(), 3u);
|
|
EXPECT_EQ(ints.take_last(), 3);
|
|
EXPECT_EQ(ints.size(), 2u);
|
|
EXPECT_EQ(ints.take_last(), 2);
|
|
EXPECT_EQ(ints.size(), 1u);
|
|
EXPECT_EQ(ints.take_last(), 1);
|
|
EXPECT_EQ(ints.size(), 0u);
|
|
|
|
ints.clear();
|
|
EXPECT_EQ(ints.size(), 0u);
|
|
}
|
|
|
|
TEST_CASE(strings)
|
|
{
|
|
Vector<String> strings;
|
|
strings.append("ABC");
|
|
strings.append("DEF");
|
|
|
|
int loop_counter = 0;
|
|
for (const String& string : strings) {
|
|
EXPECT(!string.is_null());
|
|
EXPECT(!string.is_empty());
|
|
++loop_counter;
|
|
}
|
|
|
|
loop_counter = 0;
|
|
for (auto& string : (const_cast<const Vector<String>&>(strings))) {
|
|
EXPECT(!string.is_null());
|
|
EXPECT(!string.is_empty());
|
|
++loop_counter;
|
|
}
|
|
EXPECT_EQ(loop_counter, 2);
|
|
}
|
|
|
|
TEST_CASE(strings_insert_ordered)
|
|
{
|
|
Vector<String> strings;
|
|
strings.append("abc");
|
|
strings.append("def");
|
|
strings.append("ghi");
|
|
|
|
strings.insert_before_matching("f-g", [](auto& entry) {
|
|
return "f-g" < entry;
|
|
});
|
|
|
|
EXPECT_EQ(strings[0], "abc");
|
|
EXPECT_EQ(strings[1], "def");
|
|
EXPECT_EQ(strings[2], "f-g");
|
|
EXPECT_EQ(strings[3], "ghi");
|
|
}
|
|
|
|
TEST_CASE(prepend_vector)
|
|
{
|
|
Vector<int> ints;
|
|
ints.append(1);
|
|
ints.append(2);
|
|
ints.append(3);
|
|
|
|
Vector<int> more_ints;
|
|
more_ints.append(4);
|
|
more_ints.append(5);
|
|
more_ints.append(6);
|
|
|
|
ints.prepend(move(more_ints));
|
|
|
|
EXPECT_EQ(ints.size(), 6u);
|
|
EXPECT_EQ(more_ints.size(), 0u);
|
|
|
|
EXPECT_EQ(ints[0], 4);
|
|
EXPECT_EQ(ints[1], 5);
|
|
EXPECT_EQ(ints[2], 6);
|
|
EXPECT_EQ(ints[3], 1);
|
|
EXPECT_EQ(ints[4], 2);
|
|
EXPECT_EQ(ints[5], 3);
|
|
|
|
ints.prepend(move(more_ints));
|
|
EXPECT_EQ(ints.size(), 6u);
|
|
EXPECT_EQ(more_ints.size(), 0u);
|
|
|
|
more_ints.prepend(move(ints));
|
|
EXPECT_EQ(more_ints.size(), 6u);
|
|
EXPECT_EQ(ints.size(), 0u);
|
|
}
|
|
|
|
TEST_CASE(prepend_vector_object)
|
|
{
|
|
struct SubObject {
|
|
SubObject(int v)
|
|
: value(v)
|
|
{
|
|
}
|
|
int value { 0 };
|
|
};
|
|
struct Object {
|
|
Object(NonnullOwnPtr<SubObject>&& a_subobject)
|
|
: subobject(move(a_subobject))
|
|
{
|
|
}
|
|
OwnPtr<SubObject> subobject;
|
|
};
|
|
|
|
Vector<Object> objects;
|
|
objects.empend(make<SubObject>(1));
|
|
objects.empend(make<SubObject>(2));
|
|
objects.empend(make<SubObject>(3));
|
|
|
|
EXPECT_EQ(objects.size(), 3u);
|
|
|
|
Vector<Object> more_objects;
|
|
more_objects.empend(make<SubObject>(4));
|
|
more_objects.empend(make<SubObject>(5));
|
|
more_objects.empend(make<SubObject>(6));
|
|
EXPECT_EQ(more_objects.size(), 3u);
|
|
|
|
objects.prepend(move(more_objects));
|
|
EXPECT_EQ(more_objects.size(), 0u);
|
|
EXPECT_EQ(objects.size(), 6u);
|
|
|
|
EXPECT_EQ(objects[0].subobject->value, 4);
|
|
EXPECT_EQ(objects[1].subobject->value, 5);
|
|
EXPECT_EQ(objects[2].subobject->value, 6);
|
|
EXPECT_EQ(objects[3].subobject->value, 1);
|
|
EXPECT_EQ(objects[4].subobject->value, 2);
|
|
EXPECT_EQ(objects[5].subobject->value, 3);
|
|
}
|
|
|
|
TEST_CASE(vector_compare)
|
|
{
|
|
Vector<int> ints;
|
|
Vector<int> same_ints;
|
|
|
|
for (int i = 0; i < 1000; ++i) {
|
|
ints.append(i);
|
|
same_ints.append(i);
|
|
}
|
|
|
|
EXPECT_EQ(ints.size(), 1000u);
|
|
EXPECT_EQ(ints, same_ints);
|
|
|
|
Vector<String> strings;
|
|
Vector<String> same_strings;
|
|
|
|
for (int i = 0; i < 1000; ++i) {
|
|
strings.append(String::number(i));
|
|
same_strings.append(String::number(i));
|
|
}
|
|
|
|
EXPECT_EQ(strings.size(), 1000u);
|
|
EXPECT_EQ(strings, same_strings);
|
|
}
|
|
|
|
TEST_CASE(grow_past_inline_capacity)
|
|
{
|
|
auto make_vector = [] {
|
|
Vector<String, 16> strings;
|
|
for (int i = 0; i < 32; ++i) {
|
|
strings.append(String::number(i));
|
|
}
|
|
return strings;
|
|
};
|
|
|
|
auto strings = make_vector();
|
|
|
|
EXPECT_EQ(strings.size(), 32u);
|
|
EXPECT_EQ(strings[31], "31");
|
|
|
|
strings.clear();
|
|
EXPECT_EQ(strings.size(), 0u);
|
|
EXPECT_EQ(strings.capacity(), 16u);
|
|
|
|
strings = make_vector();
|
|
|
|
strings.clear_with_capacity();
|
|
EXPECT_EQ(strings.size(), 0u);
|
|
EXPECT(strings.capacity() >= 32u);
|
|
}
|
|
|
|
BENCHMARK_CASE(vector_append_trivial)
|
|
{
|
|
// This should be super fast thanks to Vector using memmove.
|
|
Vector<int> ints;
|
|
for (int i = 0; i < 1000000; ++i) {
|
|
ints.append(i);
|
|
}
|
|
for (int i = 0; i < 100; ++i) {
|
|
Vector<int> tmp;
|
|
tmp.append(ints);
|
|
EXPECT_EQ(tmp.size(), 1000000u);
|
|
}
|
|
}
|
|
|
|
BENCHMARK_CASE(vector_remove_trivial)
|
|
{
|
|
// This should be super fast thanks to Vector using memmove.
|
|
Vector<int> ints;
|
|
for (int i = 0; i < 10000; ++i) {
|
|
ints.append(i);
|
|
}
|
|
while (!ints.is_empty()) {
|
|
ints.remove(0);
|
|
}
|
|
EXPECT_EQ(ints.size(), 0u);
|
|
}
|
|
|
|
TEST_CASE(vector_remove)
|
|
{
|
|
Vector<int> ints;
|
|
ints.append(1);
|
|
ints.append(2);
|
|
ints.append(3);
|
|
ints.append(4);
|
|
ints.append(5);
|
|
|
|
ints.remove(1);
|
|
EXPECT_EQ(ints.size(), 4u);
|
|
EXPECT_EQ(ints[0], 1);
|
|
EXPECT_EQ(ints[1], 3);
|
|
EXPECT_EQ(ints[2], 4);
|
|
EXPECT_EQ(ints[3], 5);
|
|
|
|
ints.remove(0);
|
|
EXPECT_EQ(ints.size(), 3u);
|
|
EXPECT_EQ(ints[0], 3);
|
|
EXPECT_EQ(ints[1], 4);
|
|
EXPECT_EQ(ints[2], 5);
|
|
|
|
ints.take_last();
|
|
EXPECT_EQ(ints.size(), 2u);
|
|
EXPECT_EQ(ints[0], 3);
|
|
EXPECT_EQ(ints[1], 4);
|
|
|
|
ints.take_first();
|
|
EXPECT_EQ(ints.size(), 1u);
|
|
EXPECT_EQ(ints[0], 4);
|
|
}
|
|
|
|
TEST_CASE(nonnullownptrvector)
|
|
{
|
|
struct Object {
|
|
String string;
|
|
};
|
|
NonnullOwnPtrVector<Object> objects;
|
|
|
|
objects.append(make<Object>());
|
|
EXPECT_EQ(objects.size(), 1u);
|
|
|
|
OwnPtr<Object> o = make<Object>();
|
|
objects.append(o.release_nonnull());
|
|
EXPECT(o == nullptr);
|
|
EXPECT_EQ(objects.size(), 2u);
|
|
}
|
|
|
|
TEST_CASE(insert_trivial)
|
|
{
|
|
Vector<int> ints;
|
|
ints.append(0);
|
|
ints.append(10);
|
|
ints.append(20);
|
|
ints.append(30);
|
|
ints.append(40);
|
|
ints.insert(2, 15);
|
|
EXPECT_EQ(ints.size(), 6u);
|
|
EXPECT_EQ(ints[0], 0);
|
|
EXPECT_EQ(ints[1], 10);
|
|
EXPECT_EQ(ints[2], 15);
|
|
EXPECT_EQ(ints[3], 20);
|
|
EXPECT_EQ(ints[4], 30);
|
|
EXPECT_EQ(ints[5], 40);
|
|
}
|
|
|
|
TEST_CASE(resize_initializes)
|
|
{
|
|
struct A {
|
|
A() { initialized = true; }
|
|
bool initialized { false };
|
|
};
|
|
|
|
Vector<A> ints;
|
|
ints.resize(32);
|
|
|
|
for (size_t idx = 0; idx < 32; ++idx)
|
|
EXPECT(ints[idx].initialized);
|
|
}
|
|
|
|
TEST_CASE(should_compare_vectors_of_same_type)
|
|
{
|
|
Vector<int> a {};
|
|
Vector<int> b {};
|
|
|
|
EXPECT(a == b);
|
|
EXPECT(!(a != b));
|
|
|
|
a.append(1);
|
|
EXPECT(!(a == b));
|
|
EXPECT(a != b);
|
|
|
|
b.append(1);
|
|
EXPECT(a == b);
|
|
EXPECT(!(a != b));
|
|
|
|
a.append(42);
|
|
b.append(17);
|
|
EXPECT(!(a == b));
|
|
EXPECT(a != b);
|
|
}
|
|
|
|
TEST_CASE(should_compare_vectors_of_different_inline_capacity)
|
|
{
|
|
Vector<int, 1> a {};
|
|
Vector<int, 64> b {};
|
|
|
|
EXPECT(a == b);
|
|
EXPECT(!(a != b));
|
|
|
|
a.append(1);
|
|
EXPECT(!(a == b));
|
|
EXPECT(a != b);
|
|
|
|
b.append(1);
|
|
EXPECT(a == b);
|
|
EXPECT(!(a != b));
|
|
|
|
a.append(42);
|
|
b.append(17);
|
|
EXPECT(!(a == b));
|
|
EXPECT(a != b);
|
|
}
|
|
|
|
TEST_CASE(should_compare_vectors_of_different_sizes)
|
|
{
|
|
Vector<int, 0> a {};
|
|
Vector<int, 0> b {};
|
|
|
|
EXPECT(a == b);
|
|
EXPECT(!(a != b));
|
|
|
|
// A is longer
|
|
a.append(1);
|
|
EXPECT(!(a == b));
|
|
EXPECT(a != b);
|
|
|
|
b.append(1);
|
|
EXPECT(a == b);
|
|
EXPECT(!(a != b));
|
|
|
|
// B is longer
|
|
b.append(42);
|
|
EXPECT(!(a == b));
|
|
EXPECT(a != b);
|
|
}
|
|
|
|
TEST_CASE(should_find_value)
|
|
{
|
|
Vector<int> v { 1, 2, 3, 4, 0, 6, 7, 8, 0, 0 };
|
|
|
|
const auto expected = v.begin() + 4;
|
|
|
|
EXPECT_EQ(expected, v.find(0));
|
|
}
|
|
|
|
TEST_CASE(should_find_predicate)
|
|
{
|
|
Vector<int> v { 1, 2, 3, 4, 0, 6, 7, 8, 0, 0 };
|
|
|
|
const auto expected = v.begin() + 4;
|
|
|
|
EXPECT_EQ(expected, v.find_if([](const auto v) { return v == 0; }));
|
|
}
|
|
|
|
TEST_CASE(should_find_index)
|
|
{
|
|
Vector<int> v { 1, 2, 3, 4, 0, 6, 7, 8, 0, 0 };
|
|
|
|
EXPECT_EQ(4u, v.find_first_index(0).value());
|
|
EXPECT(!v.find_first_index(42).has_value());
|
|
}
|
|
|
|
TEST_CASE(should_contain_start)
|
|
{
|
|
// Tests whether value is found if at the start of the range.
|
|
Vector<int> v { 1, 2, 3, 4, 5 };
|
|
|
|
EXPECT(v.contains_in_range(1, 0, 4));
|
|
}
|
|
|
|
TEST_CASE(should_contain_end)
|
|
{
|
|
// Tests whether value is found if at the end of the range.
|
|
Vector<int> v { 1, 2, 3, 4, 5 };
|
|
|
|
EXPECT(v.contains_in_range(5, 0, 4));
|
|
}
|
|
|
|
TEST_CASE(should_contain_range)
|
|
{
|
|
// Tests whether value is found within a range.
|
|
Vector<int> v { 1, 2, 3, 4, 5 };
|
|
|
|
EXPECT(v.contains_in_range(3, 0, 4));
|
|
}
|
|
|
|
TEST_CASE(should_not_contain_not_present)
|
|
{
|
|
// Tests whether a value that is not present is not found, as expected.
|
|
Vector<int> v { 1, 2, 3, 4, 5 };
|
|
|
|
EXPECT(!v.contains_in_range(6, 0, 4));
|
|
}
|
|
|
|
TEST_CASE(should_not_contain_present_not_in_range)
|
|
{
|
|
// Tests whether a value that is present, but not in range, is not found.
|
|
Vector<int> v { 1, 2, 3, 4, 5 };
|
|
|
|
EXPECT(!v.contains_in_range(2, 2, 4));
|
|
}
|
|
|
|
TEST_CASE(can_store_references)
|
|
{
|
|
int my_integer = 42;
|
|
Vector<int&> references;
|
|
references.append(my_integer);
|
|
references.prepend(my_integer);
|
|
EXPECT_EQ(&references.first(), &references.last());
|
|
|
|
{
|
|
Vector<int&> other_references;
|
|
other_references.append(references);
|
|
EXPECT_EQ(&other_references.first(), &my_integer);
|
|
}
|
|
|
|
{
|
|
Vector<int&> other_references;
|
|
other_references = references;
|
|
EXPECT_EQ(&other_references.first(), &my_integer);
|
|
}
|
|
|
|
{
|
|
auto it = references.find(my_integer);
|
|
EXPECT(!it.is_end());
|
|
EXPECT_EQ(*it, my_integer);
|
|
}
|
|
|
|
{
|
|
int other_integer = 42;
|
|
auto index = references.find_first_index(other_integer);
|
|
EXPECT(index.has_value());
|
|
EXPECT_EQ(index.value_or(99999u), 0u);
|
|
}
|
|
|
|
{
|
|
auto integer = 42;
|
|
EXPECT(references.contains_slow(integer));
|
|
}
|
|
|
|
{
|
|
references.remove(0);
|
|
references.ensure_capacity(10);
|
|
EXPECT_EQ(&references.take_first(), &my_integer);
|
|
}
|
|
}
|
|
|
|
TEST_CASE(reference_deletion_should_not_affect_object)
|
|
{
|
|
size_t times_deleted = 0;
|
|
struct DeleteCounter {
|
|
explicit DeleteCounter(size_t& deleted)
|
|
: deleted(deleted)
|
|
{
|
|
}
|
|
|
|
~DeleteCounter()
|
|
{
|
|
++deleted;
|
|
}
|
|
|
|
size_t& deleted;
|
|
};
|
|
|
|
{
|
|
DeleteCounter counter { times_deleted };
|
|
Vector<DeleteCounter&> references;
|
|
for (size_t i = 0; i < 16; ++i)
|
|
references.append(counter);
|
|
}
|
|
EXPECT_EQ(times_deleted, 1u);
|
|
}
|