package TestCase::Lib::SPVM::List {
use SPVM::Int;
use SPVM::List;
use SPVM::Util (equals_oarray);
use SPVM::EqualityChecker::SameObject;
sub insert : int () {
# Insert to first
{
my $x1 = "1";
my $x2 = "2";
my $x3 = "3";
my $x4 = "4";
my $list = SPVM::List->new([(object)$x1, $x2, $x3]);
$list->insert(0 => $x4);
unless (equals_oarray($list->to_array, [$x4, $x1, $x2, $x3], SPVM::EqualityChecker::SameObject->new)) {
return 0;
}
}
# Insert
{
my $x1 = "1";
my $x2 = "2";
my $x3 = "3";
my $x4 = "4";
my $list = SPVM::List->new([(object)$x1, $x2, $x3]);
$list->insert(2 => $x4);
unless (equals_oarray($list->to_array, [$x1, $x2, $x4, $x3], SPVM::EqualityChecker::SameObject->new)) {
return 0;
}
}
# Insert to last
{
my $x1 = "1";
my $x2 = "2";
my $x3 = "3";
my $x4 = "4";
my $list = SPVM::List->new([(object)$x1, $x2, $x3]);
$list->insert(3 => $x4);
unless (equals_oarray($list->to_array, [$x1, $x2, $x3, $x4], SPVM::EqualityChecker::SameObject->new)) {
return 0;
}
}
# Extend
{
my $x1 = "1";
my $x2 = "2";
my $x3 = "3";
my $x4 = "4";
my $list = SPVM::List->new([(object)$x1, $x2, $x3]);
$list->insert(0 => $x4);
}
# Exception - insert to -1
eval {
my $list = SPVM::List->new([(object)"1", "2", "3"]);
$list->insert(-1 => "2");
};
unless ($@) {
return 0;
}
$@ = undef;
# Exception - insert to length + 1
eval {
my $list = SPVM::List->new([(object)"1", "2", "3"]);
$list->insert(4 => "2");
};
unless ($@) {
return 0;
}
$@ = undef;
return 1;
}
sub remove : int () {
# Remove
{
my $x1 = "1";
my $x2 = "2";
my $x3 = "3";
my $x4 = "4";
my $list = SPVM::List->new([(object)$x1, $x2, $x3, $x4]);
my $value = $list->remove(1);
unless (equals_oarray($list->to_array, [$x1, $x3, $x4], SPVM::EqualityChecker::SameObject->new)) {
return 0;
}
unless ($value == $x2) {
return 0;
}
}
# Remove last
{
my $x1 = "1";
my $x2 = "2";
my $x3 = "3";
my $x4 = "4";
my $list = SPVM::List->new([(object)$x1, $x2, $x3]);
$list->remove(2);
unless (equals_oarray($list->to_array, [$x1, $x2], SPVM::EqualityChecker::SameObject->new)) {
return 0;
}
}
# Remove first
{
my $x1 = "1";
my $x2 = "2";
my $x3 = "3";
my $x4 = "4";
my $list = SPVM::List->new([(object)$x1, $x2, $x3]);
$list->remove(0);
unless (equals_oarray($list->to_array, [$x2, $x3], SPVM::EqualityChecker::SameObject->new)) {
return 0;
}
}
# Exception - remove to -1
eval {
my $list = SPVM::List->new([(object)"1", "2", "3"]);
$list->remove(-1);
};
unless ($@) {
return 0;
}
$@ = undef;
# Exception - remove to length
eval {
my $list = SPVM::List->new([(object)"1", "2", "3"]);
$list->remove(3);
};
unless ($@) {
return 0;
}
$@ = undef;
return 1;
}
sub check_fields : int ($list : SPVM::List, $capacity : int, $offset : int, $length : int) {
# check_fields fields
unless ($list->length == $length) {
warn("List fields mismatch.\n\t" .
"got: (length: " . $list->length . ")\n\t" .
"expected: (length: " . $length . ")");
return 0;
}
return 1;
}
sub equals_list_deeply : int ($got : SPVM::List, $expected : SPVM::List) {
unless ($got->length == $expected->length) {
warn("Length mismatch. Try check_fields() first");
return 0;
}
my $all_match = 1;
for (my $i = 0; $i < $expected->length; $i++) {
unless ($got->get($i) == $expected->get($i)) {
$all_match = 0;
}
}
if ($all_match) {
return 1;
}
else {
return 0;
}
}
sub equals_array : int ($got : object [], $expected : object []) {
if (@$got != @$expected) {
warn("Array size mismatch. got: " . @$got . ", expected: " . @$expected);
return 0;
}
for (my $i = 0; $i < @$expected; $i++) {
if ($got->[$i] != $expected->[$i]) {
warn("Array content mismatch. subscript " . $i);
return 0;
}
}
return 1;
}
sub test_ctor_default : int () {
unless (check_fields(SPVM::List->new_len(0), 16, -16, 0)) {
return 0;
}
return 1;
}
sub test_ctor_with_capacity : int () {
unless (check_fields(SPVM::List->new_capacity(20000), 20000, -20000, 0)) {
return 0;
}
eval {
SPVM::List->new_capacity(0);
};
unless ($@) {
return 0;
}
$@ = undef;
eval {
SPVM::List->new_capacity(-1);
};
unless ($@) {
return 0;
}
$@ = undef;
return 1;
}
sub test_ctor_with_array : int () {
my $list = SPVM::List->new(new object[0]);
unless (check_fields($list, 0, 0, 0)) {
return 0;
}
$list = SPVM::List->new(new object[1]);
unless (check_fields($list, 1, -1, 1)) {
return 0;
}
my $v1 = SPVM::Int->new(1);
my $vals = [(object) $v1];
$list = SPVM::List->new($vals);
unless (check_fields($list, 1, -1, 1)) {
return 0;
}
my $v2 = SPVM::Int->new(2);
$vals = new object[4];
$vals->[1] = $v1;
$vals->[3] = $v2;
$list = SPVM::List->new($vals);
unless (check_fields($list, 4, -4, 4)) {
return 0;
}
return 1;
}
sub test_length : int () {
if (SPVM::List->new_len(0)->length != 0) {
return 0;
}
if (SPVM::List->new([(object) 1])->length != 1) {
return 0;
}
return 1;
}
sub test_push : int () {
my $list = SPVM::List->new_capacity(1);
my $v1 = SPVM::Int->new(1);
$list->push($v1);
unless (check_fields($list, 1, -1, 1)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1]))) {
return 0;
}
my $v2 = SPVM::Int->new(2);
$list->push($v2);
unless (check_fields($list, 2, -2, 2)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $v2]))) {
return 0;
}
my $v3 = SPVM::Int->new(3);
$list->push($v3);
unless (check_fields($list, 4, -4, 3)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $v2, $v3]))) {
return 0;
}
# no reallocation
my $v4 = SPVM::Int->new(3);
$list->push($v4);
unless (check_fields($list, 4, -4, 4)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $v2, $v3, $v4]))) {
return 0;
}
return 1;
}
sub test_pop : int () {
my $list = SPVM::List->new([(object) SPVM::Int->new(1), undef, SPVM::Int->new(2)]);
unless (((SPVM::Int)$list->pop)->val == 2) {
return 0;
}
unless ($list->pop == undef) {
return 0;
}
unless (((SPVM::Int)$list->pop)->val == 1) {
return 0;
}
unless (check_fields($list, 3, -3, 0)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new_len(0))) {
return 0;
}
eval {
$list->pop;
};
unless ($@) {
return 0;
}
$@ = undef;
return 1;
}
sub test_unshift : int () {
my $list = SPVM::List->new_capacity(1);
my $v1 = SPVM::Int->new(1);
$list->unshift($v1);
unless (check_fields($list, 1, -1, 1)) {
return 0;
}
my $v2 = SPVM::Int->new(2);
$list->unshift($v2);
unless (check_fields($list, 2, -1, 2)) {
return 0;
}
my $v3 = SPVM::Int->new(3);
$list->unshift($v3);
unless (check_fields($list, 4, -1, 3)) {
return 0;
}
my $v4 = SPVM::Int->new(4);
$list->unshift($v4);
unless (check_fields($list, 4, -2, 4)) {
return 0;
}
my $v5 = SPVM::Int->new(5);
$list->unshift($v5);
unless (check_fields($list, 8, -1, 5)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v5, $v4, $v3, $v2, $v1]))) {
return 0;
}
return 1;
}
sub test_shift : int () {
my $list = SPVM::List->new([(object) SPVM::Int->new(1), undef, SPVM::Int->new(2)]);
unless (((SPVM::Int)$list->shift)->val == 1) {
return 0;
}
unless ($list->shift == undef) {
return 0;
}
unless (((SPVM::Int)$list->shift)->val == 2) {
return 0;
}
unless (check_fields($list, 3, -3, 0)) {
return 0;
}
eval {
$list->shift;
};
unless ($@) {
return 0;
}
$@ = undef;
unless (equals_list_deeply($list, SPVM::List->new(new object[0]))) {
return 0;
}
return 1;
}
sub test_offset_by_alternate_push_and_shift : int () {
my $list = SPVM::List->new_capacity(4);
for (my $i = 0; $i < 16; $i++) {
$list->push(1);
$list->shift;
unless (check_fields($list, 4, ($i + 1) % 4 - 4, 0)) {
return 0;
}
}
return 1;
}
sub test_offset_by_alternate_unshift_and_pop : int () {
my $list = SPVM::List->new_capacity(4);
for (my $i = 0; $i < 16; $i++) {
$list->unshift(1);
$list->pop;
unless (check_fields($list, 4, (3 - $i + 16) % 4 - 4, 0)) {
return 0;
}
}
return 1;
}
sub test_set : int () {
my $list = SPVM::List->new([(object) SPVM::Int->new(1), undef]);
my $v1 = SPVM::Int->new(2);
$list->set(0, undef);
$list->set(1, $v1);
unless (check_fields($list, 2, -2, 2)) {
return 0;
}
my $expected = new object[2];
$expected->[1] = $v1;
unless (equals_list_deeply($list, SPVM::List->new($expected))) {
return 0;
}
return 1;
}
sub test_get : int () {
my $list = SPVM::List->new([(object) SPVM::Int->new(1), undef]);
unless (((SPVM::Int)$list->get(0))->val == 1) {
return 0;
}
unless ($list->get(1) == undef) {
return 0;
}
return 1;
}
sub test_splice : int () {
my $v1 = SPVM::Int->new(1);
my $v2 = SPVM::Int->new(2);
my $v3 = SPVM::Int->new(3);
my $v4 = SPVM::Int->new(4);
my $v5 = SPVM::Int->new(5);
my $vi1 = SPVM::Int->new(10);
my $vi2 = SPVM::Int->new(11);
my $replace = [(object) $vi1, $vi2];
# extract left-inside
{
my $list = SPVM::List->new([(object) $v1, $v2, $v3, $v4, $v5]);
my $extracted = $list->splice(1, 2, undef);
unless (equals_array($extracted, [(object) $v2, $v3])) {
return 0;
}
unless (check_fields($list, 5, -5, 3)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $v4, $v5]))) {
return 0;
}
}
# extract right-inside
{
my $list = SPVM::List->new([(object) $v1, $v2, $v3, $v4, $v5]);
my $extracted = $list->splice(2, 2, undef);
unless (equals_array($extracted, [(object) $v3, $v4])) {
return 0;
}
unless (check_fields($list, 5, -5, 3)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $v2, $v5]))) {
return 0;
}
}
# replace list at left corner
{
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(0, 1, $replace);
unless (equals_array($extracted, [(object) $v1])) {
return 0;
}
unless (check_fields($list, 3, -3, 3)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $vi1, $vi2, $v2]))) {
return 0;
}
}
# replace list at right corner
{
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(1, 1, $replace);
unless (equals_array($extracted, [(object) $v2])) {
return 0;
}
unless (check_fields($list, 3, -3, 3)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $vi1, $vi2]))) {
return 0;
}
}
# replace list at center
{
my $list = SPVM::List->new([(object) $v1, $v2, $v3, $v4, $v5]);
my $extracted = $list->splice(1, 3, $replace);
unless (equals_array($extracted, [(object) $v2, $v3, $v4])) {
return 0;
}
unless (check_fields($list, 5, -5, 4)) {
return 0;
}
}
# insert list back with cut_length = 0
{
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(2, 0, $replace);
unless (equals_array($extracted, new object[0])) {
return 0;
}
unless (check_fields($list, 4, -4, 4)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $v2, $vi1, $vi2]))) {
return 0;
}
}
# insert list back with cut_length > 0
{
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(2, 10, $replace);
unless (equals_array($extracted, new object[0])) {
return 0;
}
unless (check_fields($list, 4, -4, 4)) {
return 0;
}
}
return 1;
}
sub test_splice_no_reallocation : int () {
my $v1 = SPVM::Int->new(1);
my $v2 = SPVM::Int->new(2);
my $vi1 = SPVM::Int->new(10);
my $replace = [(object) $vi1];
# extract left corner
{
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(0, 1, undef);
unless (equals_array($extracted, [(object) $v1])) {
return 0;
}
unless (check_fields($list, 2, -2, 1)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v2]))) {
return 0;
}
}
{
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(0, 1, $replace);
unless (equals_array($extracted, [(object) $v1])) {
return 0;
}
unless (check_fields($list, 2, -2, 2)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $vi1, $v2]))) {
return 0;
}
}
# extract right corner
{
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(1, 1, undef);
unless (equals_array($extracted, [(object) $v2])) {
return 0;
}
unless (check_fields($list, 2, -2, 1)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1]))) {
return 0;
}
}
{
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(1, 1, $replace);
unless (equals_array($extracted, [(object) $v2])) {
return 0;
}
unless (check_fields($list, 2, -2, 2)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $vi1]))) {
return 0;
}
}
# cut_length = replace_length = 0
my $list = SPVM::List->new([(object) $v1, $v2]);
my $extracted = $list->splice(2, 0, undef);
unless (equals_array($extracted, new object[0])) {
return 0;
}
unless (check_fields($list, 2, -2, 2)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v1, $v2]))) {
return 0;
}
return 1;
}
sub test_splice_offset_is_valid_when_removed : int () {
my $list = SPVM::List->new_capacity(2);
my $v1 = SPVM::Int->new(1);
my $v2 = SPVM::Int->new(2);
$list->unshift($v1);
$list->push($v2);
my $extracted = $list->splice(0, 1, undef);
unless (equals_array($extracted, [(object) $v1])) {
return 0;
}
unless (check_fields($list, 2, -1, 1)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $v2]))) {
return 0;
}
return 1;
}
sub test_splice_offset_is_valid_when_appended : int () {
my $list = SPVM::List->new_capacity(3);
my $v1 = SPVM::Int->new(1);
my $v2 = SPVM::Int->new(2);
my $vi1 = SPVM::Int->new(3);
my $vi2 = SPVM::Int->new(3);
$list->unshift($v1);
$list->push($v2);
my $extracted = $list->splice(0, 1, [(object) $vi1, $vi2]);
unless (equals_array($extracted, [(object) $v1])) {
return 0;
}
unless (check_fields($list, 3, -1, 3)) {
return 0;
}
unless (equals_list_deeply($list, SPVM::List->new([(object) $vi1, $vi2, $v2]))) {
return 0;
}
return 1;
}
sub test_to_array : int () {
my $list = SPVM::List->new([(object)"abc", 1, 3.14]);
my $objects = $list->to_array;
unless ($objects isa object[]) {
return 0;
}
unless ((string)($objects->[0]) eq "abc" &&
((SPVM::Int)$objects->[1])->val == 1 &&
((SPVM::Double)$objects->[2])->val == 3.14) {
return 0;
}
return 1;
}
}