#include "test.h"
using Test = TestSv<Array>;
TEST_CASE("Array", "[Array]") {
perlvars vars;
Array my(vars.av);
Sv oth_valid(vars.oav), oth_invalid(vars.hv);
SECTION("ctor") {
SECTION("empty") {
Array o;
REQUIRE(!o);
}
SECTION("from SV") {
SECTION("from undef SV") { Test::ctor(vars.undef, behaviour_t::EMPTY); }
SECTION("from number SV") { Test::ctor(vars.iv, behaviour_t::THROWS); }
SECTION("from string SV") { Test::ctor(vars.pv, behaviour_t::THROWS); }
SECTION("from RV") { Test::ctor(vars.rv, behaviour_t::THROWS); }
SECTION("from RV-OAV") { Test::ctor(vars.oavr, behaviour_t::VALID, (SV*)vars.oav); }
SECTION("from RV-OHV") { Test::ctor(vars.ohvr, behaviour_t::THROWS); }
SECTION("from AV") { Test::ctor((SV*)vars.av, behaviour_t::VALID); }
SECTION("from OAV") { Test::ctor((SV*)vars.oav, behaviour_t::VALID); }
SECTION("from HV") { Test::ctor((SV*)vars.hv, behaviour_t::THROWS); }
SECTION("from CV") { Test::ctor((SV*)vars.cv, behaviour_t::THROWS); }
SECTION("from IO") { Test::ctor((SV*)vars.io, behaviour_t::THROWS); }
}
SECTION("from AV") { Test::ctor(vars.av, behaviour_t::VALID); }
SECTION("from Array") { Test::ctor(my, behaviour_t::VALID); }
SECTION("from valid Sv") { Test::ctor(oth_valid, behaviour_t::VALID); }
SECTION("from invalid Sv") { Test::ctor(oth_invalid, behaviour_t::THROWS); }
SECTION("from ilist") {
Array o({Simple(100), Simple(200)});
REQUIRE(o);
REQUIRE(o.size() == 2);
REQUIRE(Simple(o[0]) == 100);
REQUIRE(Simple(o[1]) == 200);
}
}
SECTION("create empty") {
auto o = Array::create();
REQUIRE(o);
REQUIRE(o.get());
}
SECTION("operator=") {
auto o = Array::create();
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::THROWS); }
SECTION("RV-OAV") { Test::assign(o, vars.oavr, behaviour_t::VALID, (SV*)vars.oav); }
SECTION("RV-OHV") { Test::assign(o, vars.ohvr, behaviour_t::THROWS); }
SECTION("AV") { Test::assign(o, (SV*)vars.av, behaviour_t::VALID); }
SECTION("OAV") { Test::assign(o, (SV*)vars.oav, behaviour_t::VALID); }
SECTION("HV") { Test::assign(o, (SV*)vars.hv, behaviour_t::THROWS); }
SECTION("CV") { Test::assign(o, (SV*)vars.cv, behaviour_t::THROWS); }
SECTION("IO") { Test::assign(o, (SV*)vars.io, behaviour_t::THROWS); }
}
SECTION("AV") { Test::assign(o, vars.av, behaviour_t::VALID); }
SECTION("Array") { 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") {
Array o;
o.set(vars.iv); // no checks
REQUIRE(o);
REQUIRE(SvREFCNT(vars.iv) == 2);
REQUIRE(o.get() == vars.iv);
}
SECTION("cast") {
Array o(vars.av);
auto rcnt = SvREFCNT(vars.av);
SECTION("to SV") {
SV* sv = o;
REQUIRE(sv == (SV*)vars.av);
REQUIRE(SvREFCNT(vars.av) == rcnt);
}
SECTION("to AV") {
AV* sv = o;
REQUIRE(sv == vars.av);
REQUIRE(SvREFCNT(vars.av) == rcnt);
}
}
SECTION("get") {
Array o(vars.av);
auto rcnt = SvREFCNT(vars.av);
REQUIRE(o.get<>() == (SV*)vars.av);
REQUIRE(o.get<SV>() == (SV*)vars.av);
REQUIRE(o.get<AV>() == vars.av);
REQUIRE(SvREFCNT(vars.av) == rcnt);
}
auto arr = Array::create();
av_push(arr, newSViv(777));
av_push(arr, newSVpvs("fuckit"));
av_store(arr, 9, newSViv(555));
av_extend(arr, 100-1);
SECTION("size/top_index") {
Array o;
REQUIRE(o.size() == 0);
REQUIRE(o.top_index() == -1);
o = Array::create();
REQUIRE(o.size() == 0);
REQUIRE(o.top_index() == -1);
o = arr;
REQUIRE(o.size() == 10);
REQUIRE(o.top_index() == 9);
}
SECTION("capacity") {
Array o;
REQUIRE(o.capacity() == 0);
o = Array::create();
REQUIRE(o.capacity() == 0);
o = arr;
REQUIRE(o.capacity() >= 100);
}
SECTION("[]const") { // unsafe getter
const Array& o = arr;
CHECK(Simple(o[0]) == 777);
CHECK(Simple(o[1]) == "fuckit");
if (PERL_VERSION >= 20) {
CHECK(!o[2]);
CHECK(!o[90]);
}
}
SECTION("fetch") { // safe getter
Array o;
REQUIRE(!o.fetch(0));
REQUIRE(!o.fetch(1000));
o = Array::create();
REQUIRE(!o.fetch(0));
REQUIRE(!o.fetch(1000));
o = arr;
REQUIRE(Simple(o.fetch(0)) == 777);
if (PERL_VERSION >= 20) {
REQUIRE(!o.fetch(2));
REQUIRE(!o.fetch(90));
}
REQUIRE(!o.fetch(900));
}
SECTION("at") { // safe getter
Array o;
REQUIRE_THROWS(o.at(0));
REQUIRE_THROWS(o.at(1000));
o = Array::create();
REQUIRE_THROWS(o.at(0));
REQUIRE_THROWS(o.at(1000));
o = arr;
REQUIRE(Simple(o.at(0)) == 777);
if (PERL_VERSION >= 20) {
REQUIRE_THROWS(o.at(2));
REQUIRE_THROWS(o.at(90));
}
REQUIRE_THROWS(o.at(900));
}
SECTION("[]") { // unsafe getter
Array o = arr;
REQUIRE(Simple(o[0]) == 777);
REQUIRE(Simple(o[1]) == "fuckit");
if (PERL_VERSION >= 20) {
REQUIRE(!o[2]);
REQUIRE(!o[90]);
}
}
SECTION("[]=") { // unsafe setter
Array o = arr;
o[2] = Simple(333);
REQUIRE(Simple(o.fetch(2)) == 333);
auto icnt = SvREFCNT(vars.iv);
auto pcnt = SvREFCNT(vars.pv);
o[3] = vars.iv;
REQUIRE(o[3].get() == vars.iv);
REQUIRE(SvREFCNT(vars.iv) == icnt+1);
o[3] = vars.pv;
REQUIRE(o[3].get() == vars.pv);
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(SvREFCNT(vars.pv) == pcnt+1);
o[3] = nullptr;
REQUIRE(!o[3]);
REQUIRE(SvREFCNT(vars.pv) == pcnt);
o[3] = o[2];
REQUIRE(o.fetch(3));
REQUIRE(Simple(o.fetch(3)) == 333);
}
SECTION("store") { // safe setter
auto icnt = SvREFCNT(vars.iv);
auto pcnt = SvREFCNT(vars.pv);
Array o;
REQUIRE_THROWS(o.store(0, vars.iv));
o = arr;
o.store(0, vars.iv);
REQUIRE(o[0] == vars.iv);
REQUIRE(o.size() == 10);
o.store(5, vars.iv);
REQUIRE(o[5] == vars.iv);
REQUIRE(o.size() == 10);
o.store(10, vars.pv);
REQUIRE(o[10] == vars.pv);
REQUIRE(o.size() == 11);
o.store(95, vars.pv);
REQUIRE(o[95] == vars.pv);
REQUIRE(o.size() == 96);
o.store(1000, vars.pv);
REQUIRE(o[1000] == vars.pv);
REQUIRE(o.size() == 1001);
REQUIRE(o.capacity() >= 1001);
o.store(0, nullptr);
o.store(5, nullptr);
o.store(10, nullptr);
o.store(95, nullptr);
o.store(1000, nullptr);
REQUIRE(!o[0]);
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(SvREFCNT(vars.pv) == pcnt);
o[0] = Simple(111);
o.store(1000, o[0]);
REQUIRE(Simple(o[1000]) == 111);
}
SECTION("reserve") {
auto o = Array::create();
REQUIRE(o.capacity() == 0);
o = Array::create(10);
REQUIRE(o.capacity() >= 10);
o.reserve(1000);
REQUIRE(o.capacity() >= 1000);
}
SECTION("resize") {
auto o = Array::create();
REQUIRE(o.size() == 0);
o.resize(10);
REQUIRE(o.size() == 10);
REQUIRE(o.capacity() >= 10);
auto icnt = SvREFCNT(vars.iv);
o[9] = vars.iv;
REQUIRE(SvREFCNT(vars.iv) == icnt+1);
o.resize(9);
REQUIRE(o.size() == 9);
REQUIRE(!o.fetch(9));
REQUIRE(SvREFCNT(vars.iv) == icnt);
}
SECTION("exists") {
Array o;
REQUIRE(!o.exists(0));
o = Array::create();
REQUIRE(!o.exists(0));
o.reserve(2);
REQUIRE(!o.exists(0));
o.resize(2);
if (PERL_VERSION >= 20) REQUIRE(!o.exists(0));
o.store(1, vars.iv);
if (PERL_VERSION >= 20) REQUIRE(!o.exists(0));
REQUIRE(o.exists(1));
av_delete(o, 1, 0);
REQUIRE(!o.exists(1));
}
SECTION("del") {
Array o;
REQUIRE(!o.del(0));
o = Array::create();
REQUIRE(!o.del(0));
o.reserve(2);
REQUIRE(!o.del(0));
o.resize(2);
if (PERL_VERSION >= 20) REQUIRE(!o.del(0));
auto icnt = SvREFCNT(vars.iv);
o[0] = vars.iv;
REQUIRE(SvREFCNT(vars.iv) == icnt+1);
o[1] = vars.pv;
REQUIRE(o.del(0).get() == vars.iv);
REQUIRE(!o.exists(0));
REQUIRE(SvREFCNT(vars.iv) == icnt);
}
SECTION("create") {
SECTION("capacity") {
auto o = Array::create(50);
REQUIRE(o);
REQUIRE(o.capacity() >= 50);
}
SECTION("from SV**") {
auto o = Array::create(0, NULL);
REQUIRE(o.size() == 0);
o = Array::create(0, NULL, Array::COPY);
REQUIRE(o.size() == 0);
auto icnt = SvREFCNT(vars.iv), pcnt = SvREFCNT(vars.pv), rcnt = SvREFCNT(vars.rv);
SV* args[] = {vars.iv, vars.pv, vars.rv};
o = Array::create(3, args);
REQUIRE(o.size() == 3);
REQUIRE(o[0].get() == vars.iv);
REQUIRE(o[1].get() == vars.pv);
REQUIRE(o[2].get() == vars.rv);
REQUIRE(SvREFCNT(vars.iv) == icnt+1);
REQUIRE(SvREFCNT(vars.pv) == pcnt+1);
REQUIRE(SvREFCNT(vars.rv) == rcnt+1);
o.reset();
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(SvREFCNT(vars.pv) == pcnt);
REQUIRE(SvREFCNT(vars.rv) == rcnt);
o = Array::create(3, args, Array::COPY);
REQUIRE(o.size() == 3);
REQUIRE(o[0].get() != vars.iv);
REQUIRE(o[1].get() != vars.pv);
REQUIRE(o[2].get() != vars.rv);
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(SvREFCNT(vars.pv) == pcnt);
REQUIRE(SvREFCNT(vars.rv) == rcnt);
REQUIRE(Simple(o[0]) == SvIVX(vars.iv));
REQUIRE(Simple(o[1]).get<panda::string_view>() == SvPVX(vars.pv));
REQUIRE(Ref(o[2]).value().get() == SvRV(vars.rv));
}
SECTION("from Array") {
Array from = Array::create();
from.push(Simple(100));
auto o = Array::create(from);
REQUIRE(o);
REQUIRE(o.size() == 1);
REQUIRE(o[0] == from[0]);
}
SECTION("from ilist") {
auto o = Array::create({Simple(100), Simple(200)});
REQUIRE(o);
REQUIRE(o.size() == 2);
REQUIRE(Simple(o[0]) == 100);
REQUIRE(Simple(o[1]) == 200);
}
}
SECTION("shift") {
Array o;
REQUIRE(!o.shift());
o = Array::create();
REQUIRE(!o.shift());
auto icnt = SvREFCNT(vars.iv), pcnt = SvREFCNT(vars.pv);
o.store(1, vars.iv);
o.store(3, vars.pv);
REQUIRE(!o.shift());
REQUIRE(o.size() == 3);
auto elem = o.shift();
REQUIRE(o.size() == 2);
REQUIRE(elem.get() == vars.iv);
elem.reset();
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(!o.shift());
REQUIRE(o.size() == 1);
elem = o.shift();
REQUIRE(o.size() == 0);
REQUIRE(elem.get() == vars.pv);
elem.reset();
REQUIRE(SvREFCNT(vars.pv) == pcnt);
REQUIRE(!o.shift());
REQUIRE(o.size() == 0);
}
SECTION("pop") {
Array o;
REQUIRE(!o.pop());
o = Array::create();
REQUIRE(!o.pop());
auto icnt = SvREFCNT(vars.iv), pcnt = SvREFCNT(vars.pv);
o.store(0, vars.iv);
o.store(2, vars.pv);
o.resize(4);
REQUIRE(!o.pop());
REQUIRE(o.size() == 3);
auto elem = o.pop();
REQUIRE(o.size() == 2);
REQUIRE(elem.get() == vars.pv);
elem.reset();
REQUIRE(SvREFCNT(vars.pv) == pcnt);
REQUIRE(!o.pop());
REQUIRE(o.size() == 1);
elem = o.pop();
REQUIRE(o.size() == 0);
REQUIRE(elem.get() == vars.iv);
elem.reset();
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(!o.pop());
REQUIRE(o.size() == 0);
}
SECTION("push") {
auto o = Array::create();
o.push(Scalar());
REQUIRE(o.size() == 1);
REQUIRE(!o[0]);
auto icnt = SvREFCNT(vars.iv);
o.push(vars.iv);
REQUIRE(o.size() == 2);
REQUIRE(o[1].get() == vars.iv);
REQUIRE(SvREFCNT(vars.iv) == icnt+1);
o.push({vars.pv, vars.rv, vars.ovr});
REQUIRE(o.size() == 5);
REQUIRE(o[2].get() == vars.pv);
REQUIRE(o[3].get() == vars.rv);
REQUIRE(o[4].get() == vars.ovr);
o.push(List(Array::create({Simple(100), Simple(200)})));
REQUIRE(o.size() == 7);
REQUIRE(Simple(o[5]) == 100);
REQUIRE(Simple(o[6]) == 200);
}
SECTION("unshift") {
auto o = Array::create();
o.unshift(Scalar());
REQUIRE(o.size() == 1);
REQUIRE(!o[0]);
auto icnt = SvREFCNT(vars.iv);
o.unshift(vars.iv);
REQUIRE(o.size() == 2);
REQUIRE(o[0].get() == vars.iv);
REQUIRE(SvREFCNT(vars.iv) == icnt+1);
o.unshift({vars.pv, vars.rv, vars.ovr});
REQUIRE(o.size() == 5);
REQUIRE(o[0].get() == vars.pv);
REQUIRE(o[1].get() == vars.rv);
REQUIRE(o[2].get() == vars.ovr);
o.unshift(List(Array::create({Simple(100), Simple(200)})));
REQUIRE(o.size() == 7);
REQUIRE(Simple(o[0]) == 100);
REQUIRE(Simple(o[1]) == 200);
}
SECTION("clear/undef") {
Array o;
o.clear();
o.undef();
REQUIRE(o.size() == 0);
o = Array::create();
o.clear();
o.undef();
REQUIRE(o.size() == 0);
auto icnt = SvREFCNT(vars.iv), pcnt = SvREFCNT(vars.pv);
o.push({vars.iv, vars.pv});
auto cap = o.capacity();
REQUIRE(o.size() == 2);
o.clear();
REQUIRE(o.size() == 0);
REQUIRE(!o.exists(0));
REQUIRE(o.capacity() == cap);
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(SvREFCNT(vars.pv) == pcnt);
o.push({vars.iv, vars.pv});
REQUIRE(o.size() == 2);
o.undef();
REQUIRE(o.size() == 0);
REQUIRE(!o.exists(0));
REQUIRE(o.capacity() == 0);
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(SvREFCNT(vars.pv) == pcnt);
}
SECTION("iterate") {
Array o;
REQUIRE(o.begin() == o.end());
o = Array::create();
REQUIRE(o.begin() == o.end());
auto icnt = SvREFCNT(vars.iv);
auto pcnt = SvREFCNT(vars.pv);
o.push({vars.iv, vars.pv, vars.rv});
REQUIRE(SvREFCNT(vars.iv) == icnt+1);
auto it = o.begin();
REQUIRE(it != o.end());
REQUIRE((*it).get() == vars.iv);
REQUIRE(it[1].get() == vars.pv);
REQUIRE(SvREFCNT(vars.iv) == icnt+1);
++it;
REQUIRE(it != o.end());
REQUIRE((*it).get() == vars.pv);
REQUIRE(SvREFCNT(vars.pv) == pcnt+1);
++it;
REQUIRE(it != o.end());
REQUIRE((*it).get() == vars.rv);
++it;
REQUIRE(it == o.end());
it -= 3;
*it = vars.pv;
REQUIRE((*it).get() == vars.pv);
REQUIRE(SvREFCNT(vars.iv) == icnt);
REQUIRE(SvREFCNT(vars.pv) == pcnt+2);
}
SECTION("const iterate") {
Array src;
const Array& o = src;
REQUIRE(o.begin() == o.end());
src = Array::create();
REQUIRE(o.cbegin() == o.cend());
src.push({vars.iv, vars.pv, vars.rv});
auto it = o.cbegin();
REQUIRE(it != o.end());
REQUIRE((*it).get() == vars.iv);
REQUIRE(it[1].get() == vars.pv);
++it;
REQUIRE(it != o.end());
REQUIRE((*it).get() == vars.pv);
++it;
REQUIRE(it != o.cend());
REQUIRE((*it).get() == vars.rv);
++it;
REQUIRE(it == o.cend());
}
SECTION("multi-deref") {
auto o = Array::create({ Ref::create(Array::create({Simple(100)})), Simple(1) });
Simple res = o[0][0];
REQUIRE(res);
REQUIRE(res == 100);
o[0][0] = Simple(200);
REQUIRE(o[0][0]);
REQUIRE(Simple(o[0][0]) == 200);
REQUIRE_THROWS(o[1][0]);
auto h = Hash::create();
h["key"] = Simple(100);
o.store(2, Ref::create(h));
res = o[2]["key"];
REQUIRE(res);
REQUIRE(res == 100);
o[2]["key"] = Simple(200);
REQUIRE(o[2]["key"]);
REQUIRE(Simple(o[2]["key"]) == 200);
REQUIRE_THROWS(o[1]["key"]);
}
SECTION("front") {
Array o;
REQUIRE(!o.front());
o = Array::create();
REQUIRE(!o.front());
auto v1 = Simple(10);
o.push(v1);
REQUIRE(o.front() == v1);
auto v2 = Simple(111);
o.unshift(v2);
REQUIRE(o.front() == v2);
}
SECTION("back") {
Array o;
REQUIRE(!o.back());
o = Array::create();
REQUIRE(!o.back());
auto v1 = Simple(10);
o.push(v1);
REQUIRE(o.back() == v1);
auto v2 = Simple(111);
o.push(v2);
REQUIRE(o.back() == v2);
}
}