#include "test.h"
#include <panda/refcnt.h>
#include <catch2/reporters/catch_reporter_registrars.hpp>
#include <catch2/reporters/catch_reporter_event_listener.hpp>
TEST_PREFIX("iptr: ", "[iptr]");
namespace {
static int on_delete_calls = 0;
struct MyListener : Catch::EventListenerBase {
using EventListenerBase::EventListenerBase;
void sectionEnded( Catch::SectionStats const& /*sectionStats*/ ) override {
Tracer::reset();
on_delete_calls = 0;
}
};
CATCH_REGISTER_LISTENER(MyListener);
}
struct Test : Tracer, Refcnt {
using Tracer::Tracer;
};
struct TestChild : Test {
using Test::Test;
};
struct TestDel : Tracer, Refcntd {
using Tracer::Tracer;
void on_delete () noexcept override { on_delete_calls++; }
};
struct TestRes : Tracer, Refcntd {
using Tracer::Tracer;
bool resurected;
TestRes () : resurected() {}
void on_delete () noexcept override {
on_delete_calls++;
if (resurected) return;
retain();
resurected = true;
}
};
using TestSP = iptr<Test>;
using TestChildSP = iptr<TestChild>;
using TestDelSP = iptr<TestDel>;
using TestResSP = iptr<TestRes>;
using TestWP = weak_iptr<Test>;
using TestChildWP = weak_iptr<TestChild>;
struct A : Refcnt {};
struct AA : A {};
struct B : Refcnt {};
static int foo (iptr<A>) {
return 10;
}
static int foo (iptr<B>) {
return 20;
}
TEST("ctor") {
SECTION("empty") {
{
auto p = TestSP();
REQUIRE(!p);
REQUIRE(Tracer::ctor_calls == 0);
}
REQUIRE(Tracer::dtor_calls == 0);
}
SECTION("from object") {
{
auto p = TestSP(new Test());
REQUIRE(p);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(p->refcnt() == 1);
}
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("from iptr") {
{
auto src = TestSP(new Test());
REQUIRE(Tracer::ctor_calls == 1);
auto p(src);
REQUIRE(p);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(p->refcnt() == 2);
}
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("from foreign iptr") {
{
auto src = TestChildSP(new TestChild());
REQUIRE(Tracer::ctor_calls == 1);
TestSP p(src);
REQUIRE(p);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(p->refcnt() == 2);
}
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("move from iptr") {
{
auto src = TestSP(new Test(123));
auto p = TestSP(std::move(src));
REQUIRE(p);
REQUIRE(!src);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 123);
}
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("move from foreign iptr") {
{
auto src = TestChildSP(new TestChild(321));
auto p = TestSP(std::move(src));
REQUIRE(p);
REQUIRE(!src);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 321);
}
REQUIRE(Tracer::dtor_calls == 1);
}
}
TEST("reset") {
SECTION("no args") {
auto p = TestSP(new Test());
REQUIRE(Tracer::ctor_calls == 1);
p.reset();
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(!p);
}
SECTION("with same object") {
auto p = TestSP(new Test(1));
auto o = new Test(2);
p.reset(o);
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(p);
REQUIRE(p->value == 2);
p.reset();
REQUIRE(Tracer::dtor_calls == 2);
REQUIRE(!p);
}
SECTION("foreign object") {
auto p = TestSP(new Test(10));
auto o = new TestChild(20);
p.reset(o);
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(p);
REQUIRE(p->value == 20);
p.reset();
REQUIRE(Tracer::dtor_calls == 2);
REQUIRE(!p);
}
}
TEST("assign NULL") {
SECTION("from empty") {
TestSP p;
p = NULL;
REQUIRE(Tracer::ctor_calls == 0);
REQUIRE(Tracer::dtor_calls == 0);
REQUIRE(!p);
p.reset();
REQUIRE(Tracer::dtor_calls == 0);
}
SECTION("from object") {
auto p = TestSP(new Test());
p = NULL;
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(!p);
p.reset();
REQUIRE(Tracer::dtor_calls == 1);
}
}
TEST("assign same object") {
SECTION("from empty") {
TestSP p;
p = new Test(2);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(Tracer::dtor_calls == 0);
REQUIRE(p);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 2);
p.reset();
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("from object") {
auto p = TestSP(new Test(1));
p = new Test(2);
REQUIRE(Tracer::ctor_calls == 2);
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(p);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 2);
p.reset();
REQUIRE(Tracer::dtor_calls == 2);
}
}
TEST("assign foreign object") {
SECTION("from empty") {
TestSP p;
p = new TestChild(2);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(Tracer::dtor_calls == 0);
REQUIRE(p);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 2);
p.reset();
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("from object") {
auto p = TestSP(new Test(1));
p = new TestChild(2);
REQUIRE(Tracer::ctor_calls == 2);
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(p);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 2);
p.reset();
REQUIRE(Tracer::dtor_calls == 2);
}
}
TEST("assign same iptr") {
SECTION("from empty") {
TestSP p;
auto p2 = TestSP(new Test(2));
p = p2;
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(Tracer::dtor_calls == 0);
REQUIRE(p);
REQUIRE(p2->refcnt() == 2);
p.reset();
REQUIRE(p2->refcnt() == 1);
REQUIRE(Tracer::dtor_calls == 0);
p2.reset();
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("from object") {
auto p = TestSP(new Test(1));
auto p2 = TestSP(new Test(2));
p = p2;
REQUIRE(Tracer::ctor_calls == 2);
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(p);
REQUIRE(p2->refcnt() == 2);
p.reset();
REQUIRE(p2->refcnt() == 1);
REQUIRE(Tracer::dtor_calls == 1);
p2.reset();
REQUIRE(Tracer::dtor_calls == 2);
}
}
TEST("assign foreign iptr") {
SECTION("from empty") {
TestSP p;
auto p2 = TestChildSP(new TestChild(2));
p = p2;
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(Tracer::dtor_calls == 0);
REQUIRE(p);
REQUIRE(p2->refcnt() == 2);
p.reset();
REQUIRE(p2->refcnt() == 1);
REQUIRE(Tracer::dtor_calls == 0);
p2.reset();
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("from object") {
auto p = TestSP(new Test(1));
auto p2 = TestChildSP(new TestChild(2));
p = p2;
REQUIRE(Tracer::ctor_calls == 2);
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(p);
REQUIRE(p2->refcnt() == 2);
p.reset();
REQUIRE(p2->refcnt() == 1);
REQUIRE(Tracer::dtor_calls == 1);
p2.reset();
REQUIRE(Tracer::dtor_calls == 2);
}
}
TEST("move same iptr") {
SECTION("from empty") {
TestSP p;
auto p2 = TestSP(new Test(2));
p = std::move(p2);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(Tracer::dtor_calls == 0);
REQUIRE(p);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 2);
REQUIRE(!p2);
p.reset();
REQUIRE(Tracer::dtor_calls == 1);
p2.reset();
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("from object") {
auto p = TestSP(new Test(1));
auto p2 = TestSP(new Test(2));
p = std::move(p2);
REQUIRE(Tracer::ctor_calls == 2);
REQUIRE(Tracer::dtor_calls == 0);
REQUIRE(p);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 2);
REQUIRE(p2);
REQUIRE(p2->refcnt() == 1);
REQUIRE(p2->value == 1);
p.reset();
REQUIRE(!p);
REQUIRE(p2);
REQUIRE(p2->refcnt() == 1);
REQUIRE(p2->value == 1);
REQUIRE(Tracer::dtor_calls == 1);
p2.reset();
REQUIRE(Tracer::dtor_calls == 2);
}
}
TEST("move foreign iptr") {
SECTION("from empty") {
TestSP p;
auto p2 = TestChildSP(new TestChild(2));
p = std::move(p2);
REQUIRE(Tracer::ctor_calls == 1);
REQUIRE(Tracer::dtor_calls == 0);
REQUIRE(p);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 2);
REQUIRE(!p2);
p.reset();
REQUIRE(Tracer::dtor_calls == 1);
p2.reset();
REQUIRE(Tracer::dtor_calls == 1);
}
SECTION("from object") {
auto p = TestSP(new Test(1));
auto p2 = TestChildSP(new TestChild(2));
p = std::move(p2);
REQUIRE(Tracer::ctor_calls == 2);
REQUIRE(Tracer::dtor_calls == 1);
REQUIRE(p);
REQUIRE(p->refcnt() == 1);
REQUIRE(p->value == 2);
REQUIRE(!p2);
p.reset();
REQUIRE(!p);
REQUIRE(Tracer::dtor_calls == 2);
p2.reset();
REQUIRE(Tracer::dtor_calls == 2);
}
}
TEST("dereference") {
auto obj = new Test(123);
auto p = TestSP(obj);
REQUIRE(p->value == 123);
REQUIRE((*p).value == 123);
REQUIRE(p.get()->value == 123);
REQUIRE(p.get() == obj);
REQUIRE(((Test*)p)->value == 123);
REQUIRE((Test*)p == obj);
REQUIRE(p);
REQUIRE((bool)p == true);
}
TEST("ops") {
auto ptr = new Test(123);
auto sp1 = TestSP(ptr);
auto sp2 = TestSP(ptr);
CHECK(sp1 == sp2);
CHECK(sp1 == ptr);
}
TEST("weak ctor") {
SECTION("empty") {
TestWP empty;
CHECK_FALSE(empty);
}
SECTION("from iptr") {
SECTION("base") {
TestSP obj = new Test(123);
CHECK(obj->refcnt() == 1);
TestWP weak = obj;
CHECK(obj->refcnt() == 1);
}
SECTION("derived") {
TestChildSP obj = new TestChild(123);
TestWP weak = obj;
CHECK(obj->refcnt() == 1);
}
}
SECTION("from bad") {
SECTION("bad iptr") {
TestSP nothing;
TestWP weak(nothing);
CHECK_FALSE(weak);
}
SECTION("bad weak_iptr") {
TestWP nothing;
TestWP weak(nothing);
CHECK_FALSE(weak);
}
}
SECTION("from weak") {
TestSP base = new Test(123);
TestChildSP der = new TestChild(123);
TestWP wbase = base;
TestChildWP wder = der;
SECTION("base") {
TestWP weak = wbase;
CHECK(weak.lock() == base);
CHECK(base->refcnt() == 1);
}
SECTION("derived") {
TestWP weak = wder;
CHECK(weak.lock() == der);
CHECK(der->refcnt() == 1);
}
SECTION("move") {
TestWP moved(std::move(wbase));
CHECK(moved.weak_count() == 1);
CHECK(moved.use_count() == 1);
}
}
}
TEST("weak assign") {
TestWP empty;
TestSP base = new Test(123);
TestChildSP der = new TestChild(123);
TestWP wbase = base;
TestChildWP wder = der;
TestWP wbase2;
TestChildWP wder2;
SECTION("empty") {
TestWP e2;
e2 = empty;
CHECK_FALSE(e2);
}
SECTION("base") {
wbase2 = base;
CHECK(wbase2.lock() == base);
CHECK(wbase2.weak_count() == 2);
wbase2 = der;
CHECK(wbase2.lock() == der);
CHECK(wbase2.weak_count() == 2);
wbase2 = wbase;
CHECK(wbase2.lock() == base);
CHECK(wbase2.weak_count() == 2);
wbase2 = wder;
CHECK(wbase2.lock() == der);
CHECK(wbase2.weak_count() == 2);
}
SECTION("derived") {
wder2 = der;
CHECK(wder2.lock() == der);
wder2 = wder;
CHECK(wder2.lock() == der);
CHECK(der->refcnt() == 1);
}
SECTION("move") {
wbase2 = std::move(wbase);
CHECK(wbase2.lock() == base);
CHECK(wbase2.weak_count() == 1);
wbase2 = std::move(wder);
CHECK(wbase2.lock() == der);
CHECK(wbase2.weak_count() == 1);
}
SECTION("from bad") {
SECTION("bad iptr") {
TestSP nothing;
wbase2 = nothing;
}
SECTION("bad weak_iptr") {
TestWP nothing;
wbase2 = nothing;
}
CHECK_FALSE(wbase2);
CHECK_FALSE(wbase2.lock());
}
}
TEST("weak lock") {
TestSP obj;
TestWP weak;
CHECK_FALSE(weak.lock());
SECTION("base") {
obj = new Test(123);
}
SECTION("derived") {
obj = new TestChild(123);
}
weak = obj;
if (TestSP tmp = weak.lock()) {
CHECK(obj->refcnt() == 2);
CHECK(obj == tmp);
}
CHECK(obj->refcnt() == 1);
obj.reset();
CHECK(Tracer::dtor_calls == 1);
CHECK(weak.expired());
CHECK_FALSE(weak.lock());
}
TEST("weak use_count") {
TestWP weak;
CHECK(weak.use_count() == 0);
CHECK(weak.weak_count() == 0);
TestSP obj = new Test;
weak = obj;
CHECK(weak.use_count() == 1);
CHECK(weak.weak_count() == 1);
TestWP w2 = weak;
CHECK(weak.use_count() == 1);
CHECK(weak.weak_count() == 2);
obj.reset();
CHECK(weak.use_count() == 0);
CHECK(weak.weak_count() == 2);
}
TEST("weak generalization") {
TestSP obj = new Test;
panda::weak<TestSP> weak = obj;
CHECK(weak.use_count() == 1);
CHECK(weak.weak_count() == 1);
// check that weak<iptr<T>> is interchangeable with weak_iptr<T>
weak = panda::weak<TestSP>(TestWP());
CHECK(weak.weak_count() == 0);
weak = TestWP();
CHECK(weak.weak_count() == 0);
}
TEST("Refcntd") {
SECTION("on_delete") {
TestDelSP obj = new TestDel();
obj.reset();
CHECK(Tracer::ctor_calls == 1);
CHECK(Tracer::dtor_calls == 1);
CHECK(on_delete_calls == 1);
}
SECTION("resurect") {
auto ptr = new TestRes();
TestResSP obj = ptr;
obj.reset();
CHECK(Tracer::ctor_calls == 1);
CHECK(Tracer::dtor_calls == 0);
CHECK(on_delete_calls == 1);
ptr->release();
CHECK(Tracer::dtor_calls == 1);
CHECK(on_delete_calls == 2);
}
}
TEST("compiles") {
REQUIRE(foo(iptr<A>(nullptr)) == 10);
REQUIRE(foo(iptr<B>(nullptr)) == 20);
REQUIRE(foo(iptr<AA>(nullptr)) == 10);
}
TEST_CASE("refcnt synopsis", "[.]") {
auto pass_somewhere = [](iptr<Refcnt>) {};
struct MyType : public Refcnt {
double my_data;
};
class MyCustomType : public MyType {};
{
iptr<MyType> p = new MyType();
} // ~MyType and delete here automatically
weak_iptr<MyType> w;
{
iptr<MyType> p = new MyType();
w = p;
pass_somewhere(p);
} // ~MyType and delete here automatically
if (iptr<MyType> tmp = w.lock()) { // if object exists lock() returns a strong pointer to it
// do anything with tmp
}
iptr<MyType> p = new MyCustomType();
iptr<MyCustomType> cp = dynamic_pointer_cast<MyCustomType>(p);
}
// TEST("use weak with fdecl") {
// struct Obj;
// struct Holder {
// weak_iptr<Obj> wobj;
// };
// }