Sponsoring The Perl Toolchain Summit 2025: Help make this important event another success Learn more

#include "test.h"
using Test = TestSv<Simple>;
using panda::string_view;
template <typename T> typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, T>::type getnum (const SV* sv) { return SvIVX(sv); }
template <typename T> typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type getnum (const SV* sv) { return SvUVX(sv); }
template <typename T> typename std::enable_if<std::is_floating_point<T>::value, T>::type getnum (const SV* sv) { return SvNVX(sv); }
template <typename T> typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, T>::type oknumtype (const SV* sv) { return (bool)SvIOK(sv); }
template <typename T> typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type oknumtype (const SV* sv) { return SvUOK(sv) || SvIOK(sv); }
template <typename T> typename std::enable_if<std::is_floating_point<T>::value, T>::type oknumtype (const SV* sv) { return (bool)SvNOK(sv); }
// when policy = INCREMENT, and SV* declined, do nothing (+1 -1)
// when policy = NONE and SV* declined, it MUST be decremented
template <class T>
static void test_ctor (T val) {
Simple obj(val);
REQUIRE(getnum<T>(obj) == val);
}
template <class T>
static void test_assign (T val) {
Simple obj;
obj = val;
REQUIRE(obj);
REQUIRE(oknumtype<T>(obj));
REQUIRE(getnum<T>(obj) == val);
SV* _sv = obj;
obj = (T)0;
REQUIRE(oknumtype<T>(_sv));
REQUIRE(getnum<T>(obj) == 0);
REQUIRE((SV*)obj == _sv); // keep same SV
SV* _tmp = sv_2mortal(newSVpvs("hello"));
obj = _tmp;
REQUIRE(SvPOK(_tmp));
obj = val;
REQUIRE(oknumtype<T>(_tmp));
REQUIRE(!SvPOK(_tmp));
REQUIRE(getnum<T>(obj) == val);
REQUIRE((SV*)obj == _tmp); // keep same SV
}
template <class T>
static void test_set (T val) {
Simple obj((T)0);
obj.set(val);
REQUIRE(obj);
REQUIRE(oknumtype<T>(obj));
REQUIRE(getnum<T>(obj) == val);
}
template <class T>
static void test_cast (T val) {
Simple obj;
T r = obj;
REQUIRE(r == (T)0);
obj = val;
r = obj;
REQUIRE(r == val);
}
template <class T>
static void test_get (T val) {
Simple obj(val);
REQUIRE(obj.get<T>() == val);
}
template <class T>
static void test_as_string () {
Simple o;
REQUIRE(o.as_string<T>() == T());
o = Sv::create();
REQUIRE(o.as_string<T>() == T());
T src("epta");
o = string_view(src.data(), src.length());
REQUIRE(o.as_string<T>() == src);
}
TEST_CASE("Simple", "[Simple]") {
perlvars vars;
Simple my(vars.iv);
Sv oth_valid(vars.ov), oth_invalid(vars.av);
int ivsize = Simple(eval("use Config; $Config{ivsize}"));
SECTION("ctor") {
SECTION("empty") {
Simple obj;
REQUIRE(!obj);
}
SECTION("SV") {
SECTION("undef") { Test::ctor(vars.undef, behaviour_t::VALID); }
SECTION("number") { Test::ctor(vars.iv, behaviour_t::VALID); }
SECTION("string") { Test::ctor(vars.pv, behaviour_t::VALID); }
SECTION("OSV") { Test::ctor(vars.ov, behaviour_t::VALID); }
SECTION("RV") { Test::ctor(vars.rv, behaviour_t::THROWS); }
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("Simple") { 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("from number") {
test_ctor((int8_t)-5);
test_ctor((int16_t)-30000);
test_ctor((int32_t)1000000000);
if (ivsize == 8) test_ctor(9223372036854775807L);
test_ctor((uint8_t)255);
test_ctor((uint16_t)65535);
test_ctor((uint32_t)4000000000);
if (ivsize == 8) test_ctor(18446744073709551615LU);
test_ctor(5.5f);
test_ctor(222222222.222222);
}
SECTION("from string_view") {
Simple obj(string_view("suka"));
REQUIRE(string_view(SvPVX(obj), 4) == "suka");
REQUIRE(SvCUR(obj) == 4);
REQUIRE(obj.is_string());
}
}
SECTION("noinc") {
SECTION("undef") { Test::noinc(vars.undef, behaviour_t::VALID); }
SECTION("number") { Test::noinc(vars.iv, behaviour_t::VALID); }
SECTION("string") { Test::noinc(vars.pv, behaviour_t::VALID); }
SECTION("OSV") { Test::noinc(vars.ov, behaviour_t::VALID); }
SECTION("RV") { Test::noinc(vars.rv, behaviour_t::THROWS); }
SECTION("AV") { Test::noinc((SV*)vars.av, behaviour_t::THROWS); }
SECTION("HV") { Test::noinc((SV*)vars.hv, behaviour_t::THROWS); }
SECTION("CV") { Test::noinc((SV*)vars.cv, behaviour_t::THROWS); }
SECTION("GV") { Test::noinc((SV*)vars.gv, behaviour_t::THROWS); }
SECTION("IO") { Test::noinc((SV*)vars.io, behaviour_t::THROWS); }
}
SECTION("format") {
Simple obj = Simple::format("pi = %0.2f", 3.14157);
REQUIRE(obj.as_string() == "pi = 3.14");
}
SECTION("operator=") {
Simple o(10);
SECTION("SV") {
SECTION("undef") { Test::assign(o, vars.undef, behaviour_t::VALID); }
SECTION("number") { Test::assign(o, vars.iv, behaviour_t::VALID); }
SECTION("string") { Test::assign(o, vars.pv, behaviour_t::VALID); }
SECTION("OSV") { Test::assign(o, vars.ov, behaviour_t::VALID); }
SECTION("RV") { Test::assign(o, vars.rv, behaviour_t::THROWS); }
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("Simple") { 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("number") {
test_assign((int8_t)-5);
test_assign((int16_t)-30000);
test_assign((int32_t)1000000000);
if (ivsize == 8) test_assign(9223372036854775807L);
test_assign((uint8_t)255);
test_assign((uint16_t)65535);
test_assign((uint32_t)4000000000);
if (ivsize == 8) test_assign(18446744073709551615LU);
test_assign(5.5f);
test_assign(222222222.222222);
}
SECTION("char*") {
Simple obj(vars.iv);
obj = "abcd";
REQUIRE(string_view(SvPVX(obj), 4) == "abcd");
REQUIRE(string_view(SvPVX(vars.iv), 4) == "abcd");
REQUIRE(SvCUR(obj) == 4);
REQUIRE(obj.is_string());
}
SECTION("string_view") {
Simple obj(vars.iv);
obj = string_view("abcd");
REQUIRE(string_view(SvPVX(obj), 4) == "abcd");
REQUIRE(string_view(SvPVX(vars.iv), 4) == "abcd");
REQUIRE(SvCUR(obj) == 4);
REQUIRE(obj.is_string());
}
}
SECTION("set") {
SECTION("number") {
test_set((int8_t)-5);
test_set((int16_t)-30000);
test_set((int32_t)1000000000);
if (ivsize == 8) test_set(9223372036854775807L);
test_set((uint8_t)255);
test_set((uint16_t)65535);
test_set((uint32_t)4000000000);
if (ivsize == 8) test_set(18446744073709551615LU);
test_set(5.5f);
test_set(222222222.222222);
}
SECTION("string") {
Simple o("xxxx");
o.set("abcd");
REQUIRE(o.is_string());
REQUIRE(string_view(SvPVX(o), 4) == "abcd");
REQUIRE(SvCUR(o) == 4);
o.set(string_view("suka blya"));
REQUIRE(o.is_string());
REQUIRE(string_view(SvPVX(o), 9) == "suka blya");
REQUIRE(SvCUR(o) == 9);
}
}
SECTION("cast") {
SECTION("to number") {
test_cast((int8_t)-5);
test_cast((int16_t)-30000);
test_cast((int32_t)1000000000);
if (ivsize == 8) test_cast(9223372036854775807L);
test_cast((uint8_t)255);
test_cast((uint16_t)65535);
test_cast((uint32_t)4000000000);
if (ivsize == 8) test_cast(18446744073709551615LU);
test_cast(5.5f);
test_cast(222222222.222222);
}
SECTION("to string_view") {
Simple o;
REQUIRE(o.c_str() == nullptr);
REQUIRE((string_view)o == string_view());
o = Sv::create();
REQUIRE(strlen(o.c_str()) == 0);
REQUIRE((string_view)o == string_view());
const char* src = "epta";
o = src;
REQUIRE(strlen(o.c_str()) == 4);
REQUIRE(!strcmp(o.c_str(), src));
REQUIRE(o.c_str() != src);
REQUIRE(strlen(o.get<char*>()) == 4);
REQUIRE(!strcmp(o.get<char*>(), src));
REQUIRE(o.get<char*>() != src);
REQUIRE((string_view)o == string_view(src));
REQUIRE(((string_view)o).data() != src);
}
SECTION("to SV") {
Simple o(vars.iv);
auto cnt = SvREFCNT(vars.iv);
SV* r = o;
REQUIRE(r == vars.iv);
REQUIRE(SvREFCNT(vars.iv) == cnt);
}
}
SECTION("as_string") {
SECTION("string_view") { test_as_string<string_view>(); }
SECTION("std::string") { test_as_string<std::string>(); }
SECTION("panda::string") { test_as_string<panda::string>(); }
}
SECTION("get") {
SECTION("number") {
test_get((int8_t)-5);
test_get((int16_t)-30000);
test_get((int32_t)1000000000);
if (ivsize == 8) test_get(9223372036854775807L);
test_get((uint8_t)255);
test_get((uint16_t)65535);
test_get((uint32_t)4000000000);
if (ivsize == 8) test_get(18446744073709551615LU);
test_get(5.5f);
test_get(222222222.222222);
}
SECTION("string") {
Simple o(vars.pv);
REQUIRE(o.get<char*>() == SvPVX(vars.pv));
REQUIRE(o.get<string_view>() == string_view("hello"));
REQUIRE(o.get<string_view>().data() == SvPVX(vars.pv));
}
SECTION("SV") {
Simple o(vars.iv);
auto cnt = SvREFCNT(vars.iv);
REQUIRE(o.get<SV>() == vars.iv);
REQUIRE(SvREFCNT(vars.iv) == cnt);
}
}
SECTION("length") {
Simple o = vars.pv;
REQUIRE(o.length() == 5);
o.length(3);
REQUIRE(o.length() == 3);
REQUIRE((string_view)o == "hel");
}
SECTION("upgrade") {
Simple o = vars.iv;
o.upgrade(SVt_PVMG); // upgrade till PVMG works
REQUIRE(o.type() == SVt_PVMG);
}
SECTION("capacity / create with capacity") {
Simple o = vars.pv;
REQUIRE(o.capacity() >= 6);
o = Simple::create(100);
REQUIRE(o.capacity() >= 100);
REQUIRE(o.length() == 0);
char* buf = o.get<char*>();
*buf++ = 'j';
*buf++ = 'o';
*buf++ = 'p';
*buf++ = 'a';
*buf++ = 0;
o.length(4);
REQUIRE((string_view)o == string_view("jopa"));
}
SECTION("shared") {
Simple o("str");
Simple o2("str");
REQUIRE(o.get<char*>() != o2.get<char*>());
o = Simple::shared("str");
o2 = Simple::shared("str");
REQUIRE(o.get<char*>() == o2.get<char*>());
}
SECTION("is_shared") {
Simple o;
REQUIRE(!o.is_shared());
o = Simple("hello");
REQUIRE(!o.is_shared());
o = Simple::shared("hello");
REQUIRE(o.is_shared());
}
SECTION("hek") {
auto o = Simple::shared("world");
auto hek = o.hek();
REQUIRE(string_view(HEK_KEY(hek), HEK_LEN(hek)) == "world");
}
SECTION("hash") {
Simple o;
REQUIRE(o.hash() == 0);
o = "mystring";
U32 h;
PERL_HASH(h, "mystring", 8);
REQUIRE(o.hash() == h);
}
SECTION("const operator[]") {
const Simple o("hello world");
REQUIRE(o[0] == 'h');
REQUIRE(o[10] == 'd');
}
SECTION("operator[]") {
Simple o("hello world");
REQUIRE(o[0] == 'h');
REQUIRE(o[10] == 'd');
o[10] = 's';
REQUIRE(o[10] == 's');
REQUIRE(o.get<string_view>() == string_view("hello worls"));
}
SECTION("at") {
SECTION("string") {
Simple o("hello world");
REQUIRE(o.at(0) == 'h');
REQUIRE(o.at(10) == 'd');
REQUIRE_THROWS(o.at(11));
}
SECTION("empty obj") {
Simple o;
REQUIRE_THROWS(o.at(0));
}
SECTION("empty string") {
Simple o("");
REQUIRE_THROWS(o.at(0));
}
SECTION("number") {
Simple o(100);
REQUIRE(o.at(0) == '1');
}
}
SECTION("LV") {
auto lv = SvREFCNT_inc(SvRV(eval_pv("\\substr('suka', 1, 2)", 1)));
SECTION("detached to a value") {
auto val = Simple(lv);
CHECK(SvTYPE(val.get()) != SVt_PVLV);
CHECK(val.as_string() == "uk");
}
SECTION("refcnt") {
auto rcnt = SvREFCNT(lv);
auto val = Simple(lv);
CHECK(SvREFCNT(lv) == rcnt);
SvREFCNT_inc(lv);
rcnt = SvREFCNT(lv);
auto val2 = Simple::noinc(lv);
CHECK(SvREFCNT(lv) == rcnt - 1);
}
SvREFCNT_dec(lv);
}
}