these unused includes were identifier by clang-include-cleaner. after auditing these source files, all of the reports have been confirmed. please note, because quite a few source files relied on `utils/to_string.hh` to pull in the specialization of `fmt::formatter<std::optional<T>>`, after removing `#include <fmt/std.h>` from `utils/to_string.hh`, we have to include `fmt/std.h` directly. Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
231 lines
6.9 KiB
C++
231 lines
6.9 KiB
C++
/*
|
|
* Copyright (C) 2020-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
*/
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include "test/lib/scylla_test_case.hh"
|
|
#include <fmt/core.h>
|
|
|
|
#include "utils/allocation_strategy.hh"
|
|
#include "utils/intrusive-array.hh"
|
|
|
|
class element {
|
|
bool _head = false;
|
|
bool _tail = false;
|
|
bool _train = false;
|
|
|
|
long _data;
|
|
int *_cookie;
|
|
int *_cookie2;
|
|
|
|
public:
|
|
explicit element(long val) : _data(val), _cookie(new int(0)), _cookie2(new int(0)) { }
|
|
|
|
element(const element& other) = delete;
|
|
element(element&& other) noexcept : _head(other._head), _tail(other._tail), _train(other._train),
|
|
_data(other._data), _cookie(other._cookie), _cookie2(new int(0)) {
|
|
other._cookie = nullptr;
|
|
}
|
|
|
|
~element() {
|
|
if (_cookie != nullptr) {
|
|
delete _cookie;
|
|
}
|
|
|
|
delete _cookie2;
|
|
}
|
|
|
|
bool is_head() const noexcept { return _head; }
|
|
void set_head(bool v) noexcept { _head = v; }
|
|
bool is_tail() const noexcept { return _tail; }
|
|
void set_tail(bool v) noexcept { _tail = v; }
|
|
bool with_train() const noexcept { return _train; }
|
|
void set_train(bool v) noexcept { _train = v; }
|
|
|
|
bool operator==(long v) const { return v == _data; }
|
|
long operator*() const { return _data; }
|
|
|
|
bool bound_check(int idx, int size) {
|
|
return ((idx == 0) == is_head()) && ((idx == size - 1) == is_tail());
|
|
}
|
|
};
|
|
|
|
using test_array = intrusive_array<element>;
|
|
|
|
static bool size_check(test_array& a, size_t size, unsigned short tlen) {
|
|
return a[size - 1].is_tail() && a.size() == size &&
|
|
size_for_allocation_strategy(a) == (size + tlen) * sizeof(element) &&
|
|
((tlen != 0) == a[0].with_train()) &&
|
|
((tlen == 0) || *reinterpret_cast<unsigned short*>(&a[size]) == tlen);
|
|
}
|
|
|
|
void show(const char *pfx, test_array& a, int sz) {
|
|
int i;
|
|
|
|
fmt::print("{}", pfx);
|
|
for (i = 0; i < sz; i++) {
|
|
fmt::print("{}{}{}", a[i].is_head() ? 'H' : ' ', *a[i], a[i].is_tail() ? 'T' : ' ');
|
|
}
|
|
if (a[0].with_train()) {
|
|
fmt::print(" ~{}", *reinterpret_cast<unsigned short *>(&a[i]));
|
|
}
|
|
fmt::print("\n");
|
|
}
|
|
|
|
SEASTAR_THREAD_TEST_CASE(test_basic_construct) {
|
|
test_array array(12);
|
|
|
|
for (auto i = array.begin(); i != array.end(); i++) {
|
|
BOOST_REQUIRE(*i == 12);
|
|
}
|
|
}
|
|
|
|
test_array* grow(test_array& from, size_t nsize, int npos, long ndat) {
|
|
BOOST_REQUIRE(from.size() + 1 == nsize);
|
|
auto ptr = current_allocator().alloc<test_array>(sizeof(element) * nsize);
|
|
return new (ptr) test_array(from, test_array::grow_tag{npos}, ndat);
|
|
}
|
|
|
|
test_array* shrink(test_array& from, size_t nszie, int spos) {
|
|
BOOST_REQUIRE(from.size() - 1 == nszie);
|
|
auto ptr = current_allocator().alloc<test_array>(sizeof(element) * nszie);
|
|
return new (ptr) test_array(from, test_array::shrink_tag{spos});
|
|
}
|
|
|
|
void grow_shrink_and_check(test_array& cur, int size, int depth) {
|
|
for (int i = 0; i <= size; i++) {
|
|
long nel = size + 12;
|
|
test_array* narr = grow(cur, size + 1, i, nel);
|
|
int idx = 0;
|
|
|
|
BOOST_REQUIRE(size_check(*narr, size + 1, 0));
|
|
|
|
for (auto ni = narr->begin(); ni != narr->end(); ni++) {
|
|
if (idx == i) {
|
|
BOOST_REQUIRE(*ni == nel);
|
|
} else if (idx < i) {
|
|
BOOST_REQUIRE(*ni == *cur[idx]);
|
|
} else {
|
|
BOOST_REQUIRE(*ni == *cur[idx - 1]);
|
|
}
|
|
|
|
BOOST_REQUIRE(ni->bound_check(idx, size + 1));
|
|
idx++;
|
|
}
|
|
|
|
if (size < depth) {
|
|
grow_shrink_and_check(*narr, size + 1, depth);
|
|
}
|
|
|
|
current_allocator().destroy(narr);
|
|
}
|
|
|
|
if (size > 1) {
|
|
for (int i = 0; i < size; i++) {
|
|
test_array* narr = shrink(cur, size - 1, i);
|
|
int idx = 0;
|
|
|
|
BOOST_REQUIRE(size_check(*narr, size - 1, 0));
|
|
|
|
for (auto ni = narr->begin(); ni != narr->end(); ni++) {
|
|
if (idx == i) {
|
|
continue;
|
|
} else if (idx < i) {
|
|
BOOST_REQUIRE(*ni == *cur[idx]);
|
|
} else {
|
|
BOOST_REQUIRE(*ni == *cur[idx + 1]);
|
|
}
|
|
|
|
BOOST_REQUIRE(ni->bound_check(idx, size - 1));
|
|
idx++;
|
|
}
|
|
|
|
current_allocator().destroy(narr);
|
|
}
|
|
}
|
|
}
|
|
|
|
SEASTAR_THREAD_TEST_CASE(test_grow_shrink_construct) {
|
|
test_array array(12);
|
|
grow_shrink_and_check(array, 1, 5);
|
|
}
|
|
|
|
SEASTAR_THREAD_TEST_CASE(test_erase) {
|
|
test_array a1(10);
|
|
test_array *a2 = grow(a1, 2, 1, 20);
|
|
test_array *a3 = grow(*a2, 3, 2, 30);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < 3; j++) {
|
|
for (int k = 0; k < 2; k++) {
|
|
std::vector<int> x({10, 20, 30, 40});
|
|
test_array *a4 = grow(*a3, 4, 3, 40);
|
|
|
|
auto test_fn = [&] (int idx, int sz) {
|
|
a4->erase(idx);
|
|
x.erase(x.begin() + idx);
|
|
BOOST_REQUIRE(size_check(*a4, sz, 4 - sz));
|
|
for (int a = 0; a < sz; a++) {
|
|
BOOST_REQUIRE(x[a] == *(*a4)[a]);
|
|
}
|
|
};
|
|
|
|
test_fn(i, 3);
|
|
test_fn(j, 2);
|
|
test_fn(k, 1);
|
|
|
|
current_allocator().destroy(a4);
|
|
}
|
|
}
|
|
}
|
|
|
|
current_allocator().destroy(a3);
|
|
current_allocator().destroy(a2);
|
|
}
|
|
|
|
SEASTAR_THREAD_TEST_CASE(test_lower_bound) {
|
|
test_array a1(12);
|
|
struct compare {
|
|
std::strong_ordering operator()(const element& a, const element& b) const { return *a <=> *b; }
|
|
};
|
|
|
|
test_array *a2 = grow(a1, 2, 1, 14);
|
|
|
|
auto i = a2->lower_bound(element(13), compare{});
|
|
BOOST_REQUIRE(*i == 14 && a2->index_of(i) == 1);
|
|
|
|
test_array *a3 = grow(*a2, 3, 2, 17);
|
|
|
|
bool match;
|
|
BOOST_REQUIRE(*a3->lower_bound(element(11), compare{}, match) == 12 && !match);
|
|
BOOST_REQUIRE(*a3->lower_bound(element(12), compare{}, match) == 12 && match);
|
|
BOOST_REQUIRE(*a3->lower_bound(element(13), compare{}, match) == 14 && !match);
|
|
BOOST_REQUIRE(*a3->lower_bound(element(14), compare{}, match) == 14 && match);
|
|
BOOST_REQUIRE(*a3->lower_bound(element(15), compare{}, match) == 17 && !match);
|
|
BOOST_REQUIRE(*a3->lower_bound(element(16), compare{}, match) == 17 && !match);
|
|
BOOST_REQUIRE(*a3->lower_bound(element(17), compare{}, match) == 17 && match);
|
|
BOOST_REQUIRE(a3->lower_bound(element(18), compare{}, match) == a3->end());
|
|
|
|
current_allocator().destroy(a3);
|
|
current_allocator().destroy(a2);
|
|
}
|
|
|
|
SEASTAR_THREAD_TEST_CASE(test_from_element) {
|
|
test_array a1(12);
|
|
test_array *a2 = grow(a1, 2, 1, 14);
|
|
test_array *a3 = grow(*a2, 3, 2, 17);
|
|
|
|
element* i = &((*a3)[2]);
|
|
BOOST_REQUIRE(*i == 17);
|
|
int idx;
|
|
test_array& x = test_array::from_element(i, idx);
|
|
BOOST_REQUIRE(&x == a3 && idx == 2);
|
|
|
|
current_allocator().destroy(a3);
|
|
current_allocator().destroy(a2);
|
|
}
|