#include "test.h"
#include <sstream>
using Test = TestSv<Sv>;
template <class T>
static void test_cast (SV* good, SV* bad) {
auto rcnt = SvREFCNT(good);
Sv o(good);
T* r = o;
REQUIRE(r == (T*)good);
REQUIRE(SvREFCNT(good) == rcnt+1);
if (!bad) return;
auto rcnt_bad = SvREFCNT(bad);
o = bad;
REQUIRE(SvREFCNT(good) == rcnt);
REQUIRE(SvREFCNT(bad) == rcnt_bad+1);
REQUIRE((T*)o == nullptr);
}
template <class T>
static void test_get (SV* good, SV* bad) {
auto rcnt = SvREFCNT(good);
Sv o(good);
REQUIRE(o.get<T>() == (T*)good);
REQUIRE(SvREFCNT(good) == rcnt+1);
if (!bad) return;
auto rcnt_bad = SvREFCNT(bad);
o = bad;
REQUIRE(SvREFCNT(good) == rcnt);
REQUIRE(SvREFCNT(bad) == rcnt_bad+1);
REQUIRE(o.get<T>() == (T*)bad);
}
#define TEST(name) TEST_CASE("Sv: " name, "[Sv]")
TEST("ctor") {
perlvars vars;
SECTION("empty") {
Sv sv;
REQUIRE(!sv);
}
SECTION("undef") {
auto sv = Sv::create();
REQUIRE(sv);
REQUIRE(!SvOK(sv));
}
SECTION("SV") { Test::ctor(vars.iv, behaviour_t::VALID); }
SECTION("AV") { Test::ctor(vars.av, behaviour_t::VALID); }
SECTION("HV") { Test::ctor(vars.hv, behaviour_t::VALID); }
SECTION("CV") { Test::ctor(vars.cv, behaviour_t::VALID); }
SECTION("GV") { Test::ctor(vars.gv, behaviour_t::VALID); }
SECTION("IO") { Test::ctor(vars.io, behaviour_t::VALID); }
SECTION("Sv") { Test::ctor(Sv(vars.iv), behaviour_t::VALID); }
}
TEST("operator=") {
perlvars vars;
auto o = Sv::create();
SECTION("SV") { Test::assign(o, vars.pv, behaviour_t::VALID); }
SECTION("AV") { Test::assign(o, vars.av, behaviour_t::VALID); }
SECTION("HV") { Test::assign(o, vars.hv, behaviour_t::VALID); }
SECTION("CV") { Test::assign(o, vars.cv, behaviour_t::VALID); }
SECTION("GV") { Test::assign(o, vars.gv, behaviour_t::VALID); }
SECTION("IO") { Test::assign(o, vars.io, behaviour_t::VALID); }
SECTION("Sv") { Test::assign(o, Sv(vars.iv), behaviour_t::VALID); }
}
TEST("reset") {
perlvars vars;
Sv sv;
REQUIRE(!sv);
sv.reset();
REQUIRE(!sv);
auto cnt = SvREFCNT(vars.iv);
sv = vars.iv;
REQUIRE(SvREFCNT(vars.iv) == cnt+1);
sv.reset();
REQUIRE(!sv);
REQUIRE(SvREFCNT(vars.iv) == cnt);
}
TEST("cast") {
perlvars vars;
SECTION("to SV") { test_cast<SV>(vars.iv, NULL); }
SECTION("to AV") { test_cast<AV>((SV*)vars.av, vars.iv); }
SECTION("to HV") { test_cast<HV>((SV*)vars.hv, vars.pv); }
SECTION("to CV") { test_cast<CV>((SV*)vars.cv, vars.iv); }
SECTION("to GV") { test_cast<GV>((SV*)vars.gv, vars.iv); }
SECTION("to IO") { test_cast<IO>((SV*)vars.io, vars.iv); }
}
TEST("get") {
perlvars vars;
SECTION("SV") { test_get<SV>(vars.iv, NULL); }
SECTION("AV") { test_get<AV>((SV*)vars.av, vars.iv); }
SECTION("HV") { test_get<HV>((SV*)vars.hv, vars.pv); }
SECTION("CV") { test_get<CV>((SV*)vars.cv, vars.iv); }
SECTION("GV") { test_get<GV>((SV*)vars.gv, vars.iv); }
SECTION("IO") { test_get<IO>((SV*)vars.io, vars.iv); }
}
TEST("noinc") {
perlvars vars;
Test::noinc(vars.iv, behaviour_t::VALID);
}
TEST("to bool / defined / is_true") {
perlvars vars;
Sv sv;
REQUIRE(!sv);
REQUIRE(!sv.defined());
REQUIRE(!sv.is_true());
sv = &PL_sv_undef;
REQUIRE(sv);
REQUIRE(!sv.defined());
REQUIRE(!sv.is_true());
sv = sv_2mortal(newSViv(0));
REQUIRE(sv);
REQUIRE(sv.defined());
REQUIRE(!sv.is_true());
sv = sv_2mortal(newSViv(10));
REQUIRE(sv);
REQUIRE(sv.defined());
REQUIRE(sv.is_true());
sv = vars.av;
REQUIRE(sv);
REQUIRE(!sv.defined());
REQUIRE(!sv.is_true());
sv = vars.hv;
REQUIRE(sv);
REQUIRE(!sv.defined());
REQUIRE(!sv.is_true());
sv = vars.cv;
REQUIRE(sv);
REQUIRE(!sv.defined());
REQUIRE(!sv.is_true());
sv = vars.gv;
REQUIRE(sv);
REQUIRE(sv.defined());
REQUIRE(sv.is_true());
sv = vars.io;
REQUIRE(sv);
REQUIRE(!sv.defined());
REQUIRE(!sv.is_true());
}
TEST("type") {
perlvars vars;
Sv sv(vars.iv);
REQUIRE(sv.type() == SVt_IV);
sv = vars.pv;
REQUIRE(sv.type() == SVt_PV);
sv_setiv(sv, 10);
REQUIRE(sv.type() == SVt_PVIV);
sv = vars.av;
REQUIRE(sv.type() == SVt_PVAV);
sv = vars.hv;
REQUIRE(sv.type() == SVt_PVHV);
sv = vars.cv;
REQUIRE(sv.type() == SVt_PVCV);
sv = vars.ov;
REQUIRE(sv.type() == SVt_PVMG);
sv = vars.gv;
REQUIRE(sv.type() == SVt_PVGV);
sv = vars.io;
REQUIRE(sv.type() == SVt_PVIO);
}
TEST("readonly") {
perlvars vars;
REQUIRE(Sv::undef.readonly());
Sv sv(vars.iv);
REQUIRE(!sv.readonly());
sv.readonly(true);
REQUIRE(sv.readonly());
sv.readonly(false);
REQUIRE(!sv.readonly());
}
TEST("static undef/yes/no") {
REQUIRE(Sv::undef.readonly());
REQUIRE(Sv::yes.readonly());
REQUIRE(Sv::no.readonly());
REQUIRE(Sv::undef);
REQUIRE(Sv::yes);
REQUIRE(Sv::no);
REQUIRE(!Sv::undef.defined());
REQUIRE(Sv::yes.defined());
REQUIRE(Sv::no.defined());
REQUIRE(!Sv::undef.defined());
REQUIRE(Sv::yes.is_true());
REQUIRE(!Sv::no.is_true());
}
TEST("upgrade") {
Sv sv = Sv::create();
sv.upgrade(SVt_IV);
REQUIRE(sv.type() == SVt_IV);
SvIV_set(sv, 10);
REQUIRE(SvIVX(sv) == 10);
sv.upgrade(SVt_PV);
REQUIRE(sv.type() == SVt_PVIV);
sv = Sv::create();
sv.upgrade(SVt_PVAV);
REQUIRE(sv.type() == SVt_PVAV);
REQUIRE((AV*)sv);
sv = Sv::create();
sv.upgrade(SVt_PVHV);
REQUIRE(sv.type() == SVt_PVHV);
REQUIRE((HV*)sv);
}
TEST("operator <<") {
perlvars vars;
Sv sv = vars.iv;
std::stringstream ss;
ss << sv << ' ';
REQUIRE(ss.str() == "1000 ");
sv = vars.pv;
ss << sv << ' ';
REQUIRE(ss.str() == "1000 hello ");
sv = vars.rv;
ss << sv << ' ';
REQUIRE(ss.str().substr(0, 18) == "1000 hello SCALAR(");
}
TEST("is_scalar") {
perlvars vars;
Sv o;
REQUIRE(!o.is_scalar());
REQUIRE(Sv::undef.is_scalar());
o = vars.iv;
REQUIRE(o.is_scalar());
o = vars.pv;
REQUIRE(o.is_scalar());
o = vars.av;
REQUIRE(!o.is_scalar());
o = vars.hv;
REQUIRE(!o.is_scalar());
o = vars.rv;
REQUIRE(o.is_scalar());
o = vars.cv;
REQUIRE(!o.is_scalar());
o = vars.ov;
REQUIRE(o.is_scalar());
o = vars.stash;
REQUIRE(!o.is_scalar());
o = vars.gv;
REQUIRE(o.is_scalar());
o = vars.io;
REQUIRE(!o.is_scalar());
}
TEST("is_ref") {
perlvars vars;
Sv o;
REQUIRE(!o.is_ref());
o = vars.iv;
REQUIRE(!o.is_ref());
o = vars.pv;
REQUIRE(!o.is_ref());
o = vars.av;
REQUIRE(!o.is_ref());
o = vars.hv;
REQUIRE(!o.is_ref());
o = vars.rv;
REQUIRE(o.is_ref());
o = vars.cv;
REQUIRE(!o.is_ref());
o = vars.ov;
REQUIRE(!o.is_ref());
o = vars.stash;
REQUIRE(!o.is_ref());
o = vars.gv;
REQUIRE(!o.is_ref());
o = vars.io;
REQUIRE(!o.is_ref());
}
TEST("is_simple") {
perlvars vars;
Sv o;
REQUIRE(!o.is_simple());
REQUIRE(Sv::undef.is_simple());
o = vars.iv;
REQUIRE(o.is_simple());
o = vars.pv;
REQUIRE(o.is_simple());
o = vars.av;
REQUIRE(!o.is_simple());
o = vars.hv;
REQUIRE(!o.is_simple());
o = vars.rv;
REQUIRE(!o.is_simple());
o = vars.cv;
REQUIRE(!o.is_simple());
o = vars.ov;
REQUIRE(o.is_simple());
o = vars.stash;
REQUIRE(!o.is_simple());
o = vars.gv;
REQUIRE(!o.is_simple());
o = vars.io;
REQUIRE(!o.is_simple());
}
TEST("is_array") {
perlvars vars;
Sv o;
REQUIRE(!o.is_array());
REQUIRE(!Sv::undef.is_array());
o = vars.iv;
REQUIRE(!o.is_array());
o = vars.pv;
REQUIRE(!o.is_array());
o = vars.av;
REQUIRE(o.is_array());
o = vars.hv;
REQUIRE(!o.is_array());
o = vars.rv;
REQUIRE(!o.is_array());
o = vars.cv;
REQUIRE(!o.is_array());
o = vars.oav;
REQUIRE(o.is_array());
o = vars.stash;
REQUIRE(!o.is_array());
o = vars.gv;
REQUIRE(!o.is_array());
o = vars.io;
REQUIRE(!o.is_array());
}
TEST("is_hash") {
perlvars vars;
Sv o;
REQUIRE(!o.is_hash());
REQUIRE(!Sv::undef.is_hash());
o = vars.iv;
REQUIRE(!o.is_hash());
o = vars.pv;
REQUIRE(!o.is_hash());
o = vars.av;
REQUIRE(!o.is_hash());
o = vars.hv;
REQUIRE(o.is_hash());
o = vars.rv;
REQUIRE(!o.is_hash());
o = vars.cv;
REQUIRE(!o.is_hash());
o = vars.ohv;
REQUIRE(o.is_hash());
o = vars.stash;
REQUIRE(o.is_hash());
o = vars.gv;
REQUIRE(!o.is_hash());
o = vars.io;
REQUIRE(!o.is_hash());
}
TEST("is_sub") {
perlvars vars;
Sv o;
REQUIRE(!o.is_sub());
REQUIRE(!Sv::undef.is_sub());
o = vars.iv;
REQUIRE(!o.is_sub());
o = vars.pv;
REQUIRE(!o.is_sub());
o = vars.av;
REQUIRE(!o.is_sub());
o = vars.hv;
REQUIRE(!o.is_sub());
o = vars.rv;
REQUIRE(!o.is_sub());
o = vars.cv;
REQUIRE(o.is_sub());
o = vars.ov;
REQUIRE(!o.is_sub());
o = vars.stash;
REQUIRE(!o.is_sub());
o = vars.gv;
REQUIRE(!o.is_sub());
o = vars.io;
REQUIRE(!o.is_sub());
}
TEST("is_object") {
perlvars vars;
Sv o;
REQUIRE(!o.is_object());
REQUIRE(!Sv::undef.is_object());
o = vars.iv;
REQUIRE(!o.is_object());
o = vars.pv;
REQUIRE(!o.is_object());
o = vars.av;
REQUIRE(!o.is_object());
o = vars.hv;
REQUIRE(!o.is_object());
o = vars.rv;
REQUIRE(!o.is_object());
o = vars.cv;
REQUIRE(!o.is_object());
o = vars.ov;
REQUIRE(o.is_object());
o = vars.stash;
REQUIRE(!o.is_object());
o = vars.gv;
REQUIRE(!o.is_object());
o = vars.io;
REQUIRE(o.is_object());
}
TEST("is_stash") {
perlvars vars;
Sv o;
REQUIRE(!o.is_stash());
REQUIRE(!Sv::undef.is_stash());
o = vars.iv;
REQUIRE(!o.is_stash());
o = vars.pv;
REQUIRE(!o.is_stash());
o = vars.av;
REQUIRE(!o.is_stash());
o = vars.hv;
REQUIRE(!o.is_stash());
o = vars.rv;
REQUIRE(!o.is_stash());
o = vars.cv;
REQUIRE(!o.is_stash());
o = vars.ov;
REQUIRE(!o.is_stash());
o = vars.stash;
REQUIRE(o.is_stash());
o = vars.gv;
REQUIRE(!o.is_stash());
o = vars.io;
REQUIRE(!o.is_stash());
}
TEST("is_glob") {
perlvars vars;
Sv o;
REQUIRE(!o.is_glob());
REQUIRE(!Sv::undef.is_glob());
o = vars.iv;
REQUIRE(!o.is_glob());
o = vars.pv;
REQUIRE(!o.is_glob());
o = vars.av;
REQUIRE(!o.is_glob());
o = vars.hv;
REQUIRE(!o.is_glob());
o = vars.rv;
REQUIRE(!o.is_glob());
o = vars.cv;
REQUIRE(!o.is_glob());
o = vars.ov;
REQUIRE(!o.is_glob());
o = vars.stash;
REQUIRE(!o.is_glob());
o = vars.gv;
REQUIRE(o.is_glob());
o = vars.io;
REQUIRE(!o.is_glob());
}
TEST("operator==") {
perlvars vars;
Sv o(vars.iv);
REQUIRE(o == vars.iv);
REQUIRE(vars.iv == o);
REQUIRE(o == Sv(vars.iv));
o = vars.av;
REQUIRE(o == vars.av);
REQUIRE(vars.av == o);
o = vars.hv;
REQUIRE(o == vars.hv);
REQUIRE(vars.hv == o);
o = vars.cv;
REQUIRE(o == vars.cv);
REQUIRE(vars.cv == o);
o = vars.gv;
REQUIRE(o == vars.gv);
REQUIRE(vars.gv == o);
o = vars.io;
REQUIRE(o == vars.io);
REQUIRE(vars.io == o);
}
TEST("detach") {
perlvars vars;
Sv o;
REQUIRE(o.detach() == nullptr);
o.reset();
o = vars.iv;
auto rcnt = o.use_count();
SV* sv = o.detach();
REQUIRE(sv == vars.iv);
REQUIRE(SvREFCNT(sv) == rcnt);
o.reset();
REQUIRE(SvREFCNT(sv) == rcnt);
SvREFCNT_dec(sv);
}
TEST("eval") {
Simple res = eval("2**5");
CHECK(res == 32);
Sv err;
try {
eval("my $a = $non_existent->method();");
}
catch (PerlRuntimeException& e) {
err = e.sv;
}
CHECK(err.is_true());
}