#include "test.h"
using Test = TestSv<Ref>;
template <class T>
static void _test_create (T* sv, bool policy) {
auto rcnt = SvREFCNT(sv);
if (policy == Sv::NONE) SvREFCNT_inc(sv);
SV* rsv;
{
Ref r = Ref::create(sv, policy);
REQUIRE(r);
REQUIRE(SvREFCNT(sv) == rcnt+1);
REQUIRE(r.use_count() == 1);
rsv = r;
REQUIRE(SvREFCNT(rsv) == 1);
REQUIRE(rsv != (SV*)sv);
SvREFCNT_inc(rsv);
}
REQUIRE(SvREFCNT(rsv) == 1);
SvREFCNT_dec(rsv);
REQUIRE(SvREFCNT(sv) == rcnt);
}
template <class T>
static void test_create (T* sv) {
_test_create(sv, Sv::INCREMENT);
_test_create(sv, Sv::NONE);
}
template <class T>
static void test_create (const T& o) {
REQUIRE(o);
auto rcnt = SvREFCNT(o);
SV* rsv;
{
Ref r = Ref::create(o);
REQUIRE(r);
REQUIRE(o.use_count() == rcnt+1);
REQUIRE(r.use_count() == 1);
rsv = r;
REQUIRE(SvREFCNT(rsv) == 1);
REQUIRE(rsv != (SV*)o);
SvREFCNT_inc(rsv);
}
REQUIRE(SvREFCNT(rsv) == 1);
SvREFCNT_dec(rsv);
REQUIRE(o.use_count() == rcnt);
}
TEST_CASE("Ref", "[Ref]") {
perlvars vars;
Ref my(vars.rv);
Sv oth_valid(vars.rv), oth_invalid(vars.av);
SECTION("ctor") {
SECTION("empty") {
Ref r;
REQUIRE(!r);
}
SECTION("SV") {
SECTION("undef") { Test::ctor(vars.undef, behaviour_t::EMPTY); }
SECTION("number") { Test::ctor(vars.iv, behaviour_t::THROWS); }
SECTION("string") { Test::ctor(vars.pv, behaviour_t::THROWS); }
SECTION("RV") { Test::ctor(vars.rv, behaviour_t::VALID); }
SECTION("AV") { Test::ctor((SV*)vars.av, behaviour_t::THROWS); }
SECTION("HV") { Test::ctor((SV*)vars.hv, behaviour_t::THROWS); }
SECTION("CV") { Test::ctor((SV*)vars.cv, behaviour_t::THROWS); }
SECTION("GV") { Test::ctor((SV*)vars.gv, behaviour_t::THROWS); }
SECTION("IO") { Test::ctor((SV*)vars.io, behaviour_t::THROWS); }
}
SECTION("Ref") { Test::ctor(my, behaviour_t::VALID); }
SECTION("valid Sv") { Test::ctor(oth_valid, behaviour_t::VALID); }
SECTION("invalid Sv") { Test::ctor(oth_invalid, behaviour_t::THROWS); }
}
SECTION("create") {
SECTION("empty") {
Ref r = Ref::create();
REQUIRE(r);
REQUIRE(!SvOK(SvRV(r)));
REQUIRE(r.use_count() == 1);
REQUIRE(SvREFCNT(SvRV(r)) == 1);
}
SECTION("nullptr") {
Ref r = Ref::create((SV*)nullptr);
REQUIRE(r);
REQUIRE(!SvOK(SvRV(r)));
REQUIRE(r.use_count() == 1);
REQUIRE(SvREFCNT(SvRV(r)) == 1);
}
SECTION("SV") { test_create(vars.iv); }
SECTION("AV") { test_create(vars.av); }
SECTION("HV") { test_create(vars.hv); }
SECTION("CV") { test_create(vars.cv); }
SECTION("GV") { test_create(vars.gv); }
SECTION("IO") { test_create(vars.io); }
SECTION("Sv") { test_create(Sv(vars.pv)); }
SECTION("Scalar") { test_create(Scalar(vars.iv)); }
SECTION("Array") { test_create(Array(vars.av)); }
SECTION("Hash") { test_create(Hash(vars.hv)); }
SECTION("Object") { test_create(Object(vars.ov)); }
SECTION("Sub") { test_create(Sub(vars.cv)); }
SECTION("Stash") { test_create(Stash(vars.stash)); }
SECTION("Glob") { test_create(Glob(vars.gv)); }
SECTION("Ref") { test_create(Ref(vars.rv)); }
SECTION("Io") { test_create(Io(vars.io)); }
}
SECTION("operator=") {
auto o = Ref::create(vars.iv);
SECTION("SV") {
SECTION("undef SV") { Test::assign(o, vars.undef, behaviour_t::EMPTY); }
SECTION("number SV") { Test::assign(o, vars.iv, behaviour_t::THROWS); }
SECTION("string SV") { Test::assign(o, vars.pv, behaviour_t::THROWS); }
SECTION("RV") { Test::assign(o, vars.rv, behaviour_t::VALID); }
SECTION("AV") { Test::assign(o, (SV*)vars.av, behaviour_t::THROWS); }
SECTION("HV") { Test::assign(o, (SV*)vars.hv, behaviour_t::THROWS); }
SECTION("CV") { Test::assign(o, (SV*)vars.cv, behaviour_t::THROWS); }
SECTION("GV") { Test::assign(o, (SV*)vars.gv, behaviour_t::THROWS); }
SECTION("IO") { Test::assign(o, (SV*)vars.io, behaviour_t::THROWS); }
}
SECTION("Ref") { Test::assign(o, my, behaviour_t::VALID); }
SECTION("valid Sv") { Test::assign(o, oth_valid, behaviour_t::VALID); }
SECTION("invalid Sv") { Test::assign(o, oth_invalid, behaviour_t::THROWS); }
}
SECTION("set") {
Ref r;
r.set(vars.iv); // no checks
REQUIRE(r);
REQUIRE(SvREFCNT(vars.iv) == 2);
REQUIRE(r.get() == vars.iv);
}
SECTION("cast") {
SECTION("to SV") {
Ref r(vars.rv);
SV* sv = r;
REQUIRE(sv == vars.rv);
}
}
SECTION("get") {
SECTION("SV") {
Ref r(vars.rv);
REQUIRE(r.get<>() == vars.rv);
REQUIRE(r.get<SV>() == vars.rv);
}
}
SECTION("get ref") {
SV* rv = sv_2mortal(newRV(vars.iv));
Ref r(rv);
REQUIRE(r.value().get() == vars.iv);
REQUIRE(r.value<Sv>().get() == vars.iv);
REQUIRE(r.value<Scalar>().get() == vars.iv);
REQUIRE(r.value<Simple>().get() == vars.iv);
REQUIRE_THROWS_AS(r.value<Array>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Hash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Sub>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Object>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Stash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Glob>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Ref>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Io>().get(), std::invalid_argument);
SvRV_set(rv, (SV*)vars.av);
REQUIRE(r.value().get<AV>() == vars.av);
REQUIRE_THROWS_AS(r.value<Scalar>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Simple>().get(), std::invalid_argument);
REQUIRE(r.value<Array>().get<AV>() == vars.av);
REQUIRE_THROWS_AS(r.value<Hash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Sub>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Object>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Stash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Glob>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Ref>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Io>().get(), std::invalid_argument);
SvRV_set(rv, (SV*)vars.hv);
REQUIRE(r.value().get<HV>() == vars.hv);
REQUIRE_THROWS_AS(r.value<Scalar>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Simple>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Array>().get(), std::invalid_argument);
REQUIRE(r.value<Hash>().get<HV>() == vars.hv);
REQUIRE_THROWS_AS(r.value<Sub>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Object>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Stash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Glob>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Ref>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Io>().get(), std::invalid_argument);
SvRV_set(rv, (SV*)vars.cv);
REQUIRE(r.value().get<CV>() == vars.cv);
REQUIRE_THROWS_AS(r.value<Scalar>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Simple>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Array>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Hash>().get(), std::invalid_argument);
REQUIRE(r.value<Sub>().get<CV>() == vars.cv);
REQUIRE_THROWS_AS(r.value<Object>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Stash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Glob>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Ref>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Io>().get(), std::invalid_argument);
SvRV_set(rv, (SV*)vars.ov);
REQUIRE(r.value().get<SV>() == vars.ov);
REQUIRE(r.value<Scalar>().get() == vars.ov);
REQUIRE(r.value<Simple>().get() == vars.ov);
REQUIRE_THROWS_AS(r.value<Array>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Hash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Sub>().get(), std::invalid_argument);
REQUIRE(r.value<Object>().get() == vars.ov);
REQUIRE_THROWS_AS(r.value<Stash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Glob>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Ref>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Io>().get(), std::invalid_argument);
SvRV_set(rv, (SV*)vars.stash);
REQUIRE(r.value().get<HV>() == vars.stash);
REQUIRE_THROWS_AS(r.value<Scalar>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Simple>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Array>().get(), std::invalid_argument);
REQUIRE(r.value<Hash>().get<HV>() == vars.stash);
REQUIRE_THROWS_AS(r.value<Sub>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Object>().get(), std::invalid_argument);
REQUIRE(r.value<Stash>().get<HV>() == vars.stash);
REQUIRE_THROWS_AS(r.value<Glob>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Ref>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Io>().get(), std::invalid_argument);
SvRV_set(rv, (SV*)vars.gv);
REQUIRE(r.value().get<GV>() == vars.gv);
REQUIRE(r.value<Scalar>().get<GV>() == vars.gv);
REQUIRE_THROWS_AS(r.value<Simple>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Array>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Hash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Sub>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Object>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Stash>().get(), std::invalid_argument);
REQUIRE(r.value<Glob>().get<GV>() == vars.gv);
REQUIRE_THROWS_AS(r.value<Ref>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Io>().get(), std::invalid_argument);
SvRV_set(rv, vars.rv);
REQUIRE(r.value().get() == vars.rv);
REQUIRE(r.value<Scalar>().get() == vars.rv);
REQUIRE_THROWS_AS(r.value<Simple>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Array>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Hash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Sub>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Object>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Stash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Glob>().get(), std::invalid_argument);
REQUIRE(r.value<Ref>().get() == vars.rv);
REQUIRE_THROWS_AS(r.value<Io>().get(), std::invalid_argument);
SvRV_set(rv, (SV*)vars.io);
REQUIRE(r.value().get<IO>() == vars.io);
REQUIRE_THROWS_AS(r.value<Scalar>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Simple>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Array>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Hash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Sub>().get(), std::invalid_argument);
REQUIRE(r.value<Object>().get() == (SV*)vars.io);
REQUIRE_THROWS_AS(r.value<Stash>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Glob>().get(), std::invalid_argument);
REQUIRE_THROWS_AS(r.value<Ref>().get(), std::invalid_argument);
REQUIRE(r.value<Io>().get<IO>() == vars.io);
SvRV_set(rv, vars.iv); // do not remove, or double free will occur
}
SECTION("set ref") {
SV* rv = sv_2mortal(newRV(vars.iv));
Ref r(rv);
REQUIRE(SvREFCNT(vars.iv) == 2);
REQUIRE(SvREFCNT(vars.pv) == 1);
r.value(vars.pv);
REQUIRE(SvREFCNT(vars.iv) == 1);
REQUIRE(SvREFCNT(vars.pv) == 2);
r.value(nullptr);
REQUIRE(SvREFCNT(vars.pv) == 1);
REQUIRE(r.value());
REQUIRE(!r.value().defined());
REQUIRE(r.get() == rv);
r.reset();
REQUIRE(!r);
REQUIRE(!r.get());
auto cnt = SvREFCNT(vars.av);
r.value(vars.av);
REQUIRE(r);
REQUIRE(r.get());
REQUIRE(r.get() != rv);
REQUIRE(SvREFCNT(vars.av) == cnt+1);
}
}