#include "test.h" #include using Test = TestSv; template 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 static void test_get (SV* good, SV* bad) { auto rcnt = SvREFCNT(good); Sv o(good); REQUIRE(o.get() == (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*)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(vars.iv, NULL); } SECTION("to AV") { test_cast((SV*)vars.av, vars.iv); } SECTION("to HV") { test_cast((SV*)vars.hv, vars.pv); } SECTION("to CV") { test_cast((SV*)vars.cv, vars.iv); } SECTION("to GV") { test_cast((SV*)vars.gv, vars.iv); } SECTION("to IO") { test_cast((SV*)vars.io, vars.iv); } } TEST("get") { perlvars vars; SECTION("SV") { test_get(vars.iv, NULL); } SECTION("AV") { test_get((SV*)vars.av, vars.iv); } SECTION("HV") { test_get((SV*)vars.hv, vars.pv); } SECTION("CV") { test_get((SV*)vars.cv, vars.iv); } SECTION("GV") { test_get((SV*)vars.gv, vars.iv); } SECTION("IO") { test_get((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()); }