PrusaSlicer/tests/libslic3r/test_anyptr.cpp
2025-02-06 15:55:09 +01:00

199 lines
5.7 KiB
C++

#include <catch2/catch_test_macros.hpp>
#include <libslic3r/libslic3r.h>
#include <libslic3r/AnyPtr.hpp>
#include <test_utils.hpp>
class Foo
{
public:
virtual ~Foo() = default;
virtual void set_foo(int) = 0;
virtual int get_foo() const = 0;
};
class Bar: public Foo
{
int m_i = 0;
public:
virtual void set_foo(int i) { m_i = i; }
virtual int get_foo() const { return m_i; };
};
class BarPlus: public Foo {
int m_i = 0;
public:
virtual void set_foo(int i) { m_i = i + 1; }
virtual int get_foo() const { return m_i; };
};
TEST_CASE("Testing AnyPtr", "[anyptr]") {
using Slic3r::AnyPtr;
SECTION("Construction with various valid arguments using operator=")
{
auto args = std::make_tuple(nullptr,
AnyPtr<Foo>{nullptr},
AnyPtr{static_cast<Foo *>(nullptr)},
AnyPtr{static_cast<Bar *>(nullptr)},
AnyPtr{static_cast<BarPlus *>(nullptr)},
AnyPtr<Foo>{},
AnyPtr<Bar>{},
AnyPtr<BarPlus>{},
static_cast<Foo *>(nullptr),
static_cast<Bar *>(nullptr),
static_cast<BarPlus *>(nullptr));
auto check_ptr = [](auto &ptr) {
REQUIRE(!ptr);
REQUIRE(!ptr.is_owned());
auto shp = ptr.get_shared_cpy();
REQUIRE(!shp);
};
SECTION("operator =") {
Slic3r::for_each_in_tuple([&check_ptr](auto &arg){
AnyPtr<const Foo> ptr = std::move(arg);
check_ptr(ptr);
}, args);
}
SECTION("move construction")
{
Slic3r::for_each_in_tuple([&check_ptr](auto &arg){
AnyPtr<const Foo> ptr{std::move(arg)};
check_ptr(ptr);
}, args);
}
}
GIVEN("A polymorphic base class type Foo") {
WHEN("Creating a subclass on the stack") {
Bar bar;
auto val = random_value(-100, 100);
bar.set_foo(val);
THEN("Storing a raw pointer in an AnyPtr<Foo> should be valid "
"until the object is not destroyed")
{
AnyPtr<Foo> ptr = &bar;
auto val2 = random_value(-100, 100);
ptr->set_foo(val2);
REQUIRE(ptr->get_foo() == val2);
}
THEN("Storing a raw pointer in an AnyPtr<const Foo> should be "
"valid until the object is not destroyed")
{
AnyPtr<const Foo> ptr{&bar};
REQUIRE(ptr->get_foo() == val);
}
}
}
GIVEN("An empty AnyPtr of type Foo")
{
AnyPtr<Foo> ptr;
WHEN("Re-assigning a new unique_ptr of object of type Bar to ptr")
{
auto bar = std::make_unique<Bar>();
auto val = random_value(-100, 100);
bar->set_foo(val);
ptr = std::move(bar);
THEN("the ptr should contain the new object and should own it")
{
REQUIRE(ptr->get_foo() == val);
REQUIRE(ptr.is_owned());
}
}
WHEN("Re-assigning a new unique_ptr of object of type BarPlus to ptr")
{
auto barplus = std::make_unique<BarPlus>();
auto val = random_value(-100, 100);
barplus->set_foo(val);
ptr = std::move(barplus);
THEN("the ptr should contain the new object and should own it")
{
REQUIRE(ptr->get_foo() == val + 1);
REQUIRE(ptr.is_owned());
}
THEN("copying the stored object into a shared_ptr should be invalid")
{
std::shared_ptr<Foo> shptr = ptr.get_shared_cpy();
REQUIRE(!shptr);
}
THEN("copying the stored object into a shared_ptr after calling "
"convert_unique_to_shared should be valid")
{
ptr.convert_unique_to_shared();
std::shared_ptr<Foo> shptr = ptr.get_shared_cpy();
REQUIRE(shptr);
REQUIRE(shptr->get_foo() == val + 1);
}
}
}
GIVEN("A vector of AnyPtr<Foo> pointer to random Bar or BarPlus objects")
{
std::vector<AnyPtr<Foo>> ptrs;
auto N = random_value(size_t(1), size_t(10));
INFO("N = " << N);
std::generate_n(std::back_inserter(ptrs), N, []{
auto v = random_value(0, 1);
std::unique_ptr<Foo> ret;
if (v)
ret = std::make_unique<Bar>();
else
ret = std::make_unique<BarPlus>();
return ret;
});
WHEN("moving the whole array into a vector of AnyPtr<const Foo>")
{
THEN("the move should be valid")
{
std::vector<AnyPtr<const Foo>> constptrs;
std::vector<int> vals;
std::transform(ptrs.begin(), ptrs.end(),
std::back_inserter(vals),
[](auto &p) { return p->get_foo(); });
std::move(ptrs.begin(), ptrs.end(), std::back_inserter(constptrs));
REQUIRE(constptrs.size() == N);
REQUIRE(ptrs.size() == N);
REQUIRE(std::all_of(ptrs.begin(), ptrs.end(), [](auto &p) { return !p; }));
for (size_t i = 0; i < N; ++i) {
REQUIRE(vals[i] == constptrs[i]->get_foo());
}
}
}
}
}