class TestCase::JSON {
use JSON;
use List;
use Hash;
use Fn;
use Sort;
use Point;
static method _equals : int ($lhs : object, $rhs : object) {
if ($lhs == undef) {
unless ($rhs == undef) {
return 0;
}
}
elsif ($lhs isa Hash) {
unless ($rhs isa Hash) {
return 0;
}
my $l_hash = (Hash)$lhs;
my $r_hash = (Hash)$lhs;
my $l_keys = $l_hash->keys;
my $r_keys = $r_hash->keys;
if (@$l_keys == 0) {
unless (@$r_keys == 0) {
return 0;
}
}
else {
Sort->sort_string_asc($l_keys, 0, scalar @$l_keys);
Sort->sort_string_asc($r_keys, 0, scalar @$r_keys);
for (my $key_index = 0; $key_index < @$l_keys; ++$key_index) {
my $key = $l_keys->[$key_index];
unless ($l_keys->[$key_index] eq $r_keys->[$key_index]) {
return 0;
}
unless (&_equals($l_hash->get($key), $r_hash->get($key))) {
return 0;
}
}
}
}
elsif ($lhs isa List) {
unless ($rhs isa List) {
return 0;
}
my $l_list = (List)$lhs;
my $r_list = (List)$rhs;
unless ($l_list->length == $r_list->length) {
return 0;
}
my $length = $l_list->length;
for (my $list_index = 0; $list_index < $length; ++$list_index) {
unless (&_equals($l_list->get($list_index), $r_list->get($list_index))) {
return 0;
}
}
}
elsif ($lhs isa string) {
unless ($rhs isa string) {
return 0;
}
my $l_str = (string)$lhs;
my $r_str = (string)$rhs;
unless ($l_str eq $r_str) {
return 0;
}
}
elsif ($lhs isa Int) {
unless ($rhs isa Int) {
return 0;
}
my $l_int = (Int)$lhs;
my $r_int = (Int)$rhs;
unless ($l_int->value == $r_int->value) {
return 0;
}
}
elsif ($lhs isa Bool) {
unless ($rhs isa Bool) {
return 0;
}
my $l_bool = (Bool)$lhs;
my $r_bool = (Bool)$rhs;
unless ($l_bool->value == $r_bool->value) {
return 0;
}
}
elsif ($lhs isa Double) {
unless ($rhs isa Double) {
return 0;
}
my $l_dbl = (Double)$lhs;
my $r_dbl = (Double)$rhs;
unless ($l_dbl->value == $r_dbl->value) {
return 0;
}
}
else {
die "Not implemented type is used in \$lhs";
}
return 1;
}
static method encode_json_hash : int () {
my $json = JSON->new;
{
my $input = Hash->new({});
my $got = $json->encode($input);
my $expected = "{}";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = Hash->new({"int" => 42 });
my $got = $json->encode($input);
my $expected = "{\"int\":42}";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = Hash->new({"string" => "vstr"});
my $got = $json->encode($input);
my $expected = "{\"string\":\"vstr\"}";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = Hash->new({"double" => 0.123});
my $got = $json->encode($input);
my $expected = "{\"double\":0.123}";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = Hash->new({"bool_true" => true});
my $got = $json->encode($input);
my $expected = "{\"bool_true\":true}";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = Hash->new({"bool_false" => false});
my $got = $json->encode($input);
my $expected = "{\"bool_false\":false}";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = Hash->new({
"bool" => false,
"double" => 0.1,
"int" => 1,
"str" => "hoge",
});
my $got = $json->encode($input);
my $expected = "{\"bool\":false,\"double\":0.1,\"int\":1,\"str\":\"hoge\"}";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = Hash->new({
"A" => Hash->new({
"B" => 1,
"C" => Hash->new({
"D" => 0.1,
"E" => true,
"F" => "str",
"G" => List->new([(object)
"abc",
123,
Hash->new({
"H" => "val",
}),
]),
})
}),
"I" => "EOF",
});
my $got = $json->encode($input);
my $expected = "{\"A\":{\"B\":1,\"C\":{\"D\":0.1,\"E\":true,\"F\":\"str\",\"G\":[\"abc\",123,{\"H\":\"val\"}]}},\"I\":\"EOF\"}";
unless ($got eq $expected) {
return 0;
}
return 1;
}
return 1;
}
static method encode_json_object : int () {
my $json = JSON->new;
{
my $input = Point->new(1, 2);
eval { $json->encode($input); };
unless (Fn->contains($@, "The \$spvm_data contains a value of an invalid type")) {
return 0;
}
}
return 1;
}
static method decode_json_hash : int () {
my $json = JSON->new;
{
my $input = "{}";
my $got = $json->decode($input);
my $expected = Hash->new({});
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "{\"string\":\"vstr\"}";
my $got = $json->decode($input);
my $expected = Hash->new({"string" => "vstr"});
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "{\"double\":0.123}";
my $got = $json->decode($input);
my $expected = Hash->new({"double" => 0.123});
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "{\"bool_true\":true}";
my $got = $json->decode($input);
my $expected = Hash->new({"bool_true" => true});
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "{\"bool_false\":false}";
my $got = $json->decode($input);
my $expected = Hash->new({"bool_false" => false});
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "{\"double\":0.1,\"bool\":false,\"int\":1,\"str\":\"hoge\"}";
my $got = $json->decode($input);
my $expected = Hash->new({
"bool" => false,
"double" => 0.1,
"str" => "hoge",
});
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = " { \"key\" \n\t: 123\t,\n\t\t\"list\" :\n[\t1\t,\r2\t,\t3\n]}\r\r";
my $got = $json->decode($input);
my $expected = Hash->new({
"key" => 123.0,
"list" => List->new([(object) 1.0, 2.0, 3.0])
});
unless (&_equals($got, $expected)) {
return 0;
}
}
# Duplicated keys - overwritten the first value
{
my $input = "{\"foo\":\"a\", \"foo\":\"b\"}";
my $got = $json->decode($input);
my $expected = Hash->new({"foo" => "b"});
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "{\"A\":{\"B\":1,\"C\":{\"D\":0.1,\"E\":true,\"F\":\"str\",\"G\":[\"abc\",123,{\"H\":\"val\"}]}},\"I\":\"EOF\"}";
my $got = $json->decode($input);
my $expected = Hash->new({
"A" => Hash->new({
"B" => 1.0,
"C" => Hash->new({
"D" => 0.1,
"E" => true,
"F" => "str",
"G" => List->new([(object)
"abc",
123.0,
Hash->new({
"H" => "val",
}),
]),
})
}),
"I" => "EOF",
});
unless (&_equals($got, $expected)) {
return 0;
}
return 1;
}
return 1;
}
static method encode_json_list : int () {
my $json = JSON->new;
{
my $input = List->new([]);
my $got = $json->encode($input);
my $expected = "[]";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = List->new([(object) 1, 2, 3]);
my $got = $json->encode($input);
my $expected = "[1,2,3]";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = List->new([(object) "abc", "def"]);
my $got = $json->encode($input);
my $expected = "[\"abc\",\"def\"]";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = List->new([(object) 0, 3.14, "abc", true]);
my $got = $json->encode($input);
my $expected = "[0,3.14,\"abc\",true]";
unless ($got eq $expected) {
return 0;
}
}
return 1;
}
static method decode_json_list : int () {
my $json = JSON->new;
{
my $input = "[]";
my $got = $json->decode($input);
my $expected = List->new([]);
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "[3,2,1]";
my $got = $json->decode($input);
my $expected = List->new([(object) 3.0, 2.0, 1.0]);
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "[\"def\",\"abc\"]";
my $got = $json->decode($input);
my $expected = List->new([(object) "def", "abc"]);
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "[0,3.14,\"abc\",true]";
my $got = $json->decode($input);
my $expected = List->new([(object) 0.0, 3.14, "abc", true]);
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = " [ 3.14\n, true \t , \"a\" ] ";
my $got = $json->decode($input);
my $expected = List->new([(object) 3.14, true, "a"]);
unless (&_equals($got, $expected)) {
return 0;
}
}
return 1;
}
static method encode_json_number : int () {
my $json = JSON->new;
my $test_case = [
0.123, -0.123, 3.14, -3.14, 123.987, -123.987,
1.23456e+1, 1.23456e-1, 1.23456e+08, 1.23456e-08, 1.23456e+008, 1.23456e-008,
1.23456e+018, 1.23456e-018, 9.9e-100, 9.9e+300, -1.23e+123
];
for (my $i = 0; $i < @$test_case; ++$i) {
my $input = Double->new($test_case->[$i]);
my $got = $json->encode($input);
my $expected = (string)$test_case->[$i];
warn "[Test Output]Got:$got,Expected:$expected";
unless ($got eq $expected) {
return 0;
}
}
# Byte
{
my $input = Fn->BYTE_MIN;
my $got = $json->encode($input);
my $expected = "-128";
unless ($got eq $expected) {
return 0;
}
}
# Short
{
my $input = Fn->SHORT_MIN;
my $got = $json->encode($input);
my $expected = "-32768";
unless ($got eq $expected) {
return 0;
}
}
# Int
{
my $input = Fn->INT_MIN;
my $got = $json->encode($input);
my $expected = "-2147483648";
unless ($got eq $expected) {
return 0;
}
}
# Long
{
my $input = Fn->LONG_MIN;
my $got = $json->encode($input);
my $expected = "-9223372036854775808";
unless ($got eq $expected) {
return 0;
}
}
# Exceptions
{
# Float - Infinity
{
my $input = Math->INFINITYF;
eval { $json->encode($input); }
unless (Fn->contains($@, "The \$spvm_data cannot contain an inifinity float value. The found depth is 0.")) {
return 0;
}
}
# Float - NaN
{
my $input = Math->NANF;
eval { $json->encode($input); }
unless (Fn->contains($@, "The \$spvm_data cannot contain a NaN float value. The found depth is 0.")) {
return 0;
}
}
# Double - Infinity
{
my $input = Math->INFINITY;
eval { $json->encode($input); }
unless (Fn->contains($@, "The \$spvm_data cannot contain an inifinity double value. The found depth is 0.")) {
return 0;
}
}
# Double - NaN
{
my $input = Math->NAN;
eval { $json->encode($input); }
unless (Fn->contains($@, "The \$spvm_data cannot contain a NaN double value. The found depth is 0.")) {
return 0;
}
}
# Invalid Type
{
my $input = Point->new;
eval { $json->encode($input); }
unless (Fn->contains($@, "The \$spvm_data contains a value of an invalid type. The type is Point. The found depth is 0.")) {
return 0;
}
}
}
return 1;
}
static method decode_json_number : int () {
my $json = JSON->new;
my $test_case = [
0.123, -0.123, 3.14, -3.14, 123.987, -123.987,
1.23456e+1, 1.23456e-1, 1.23456e+08, 1.23456e-08, 1.23456e+008, 1.23456e-008,
1.23456e+018, 1.23456e-018, 9.9e-100, 9.9e+300, -1.23e+123
];
for (my $i = 0; $i < @$test_case; ++$i) {
my $input = "" . $test_case->[$i];
my $got = "" . ((Double)$json->decode($input))->value;
my $expected = "" . $test_case->[$i];
unless ($got eq $expected) {
return 0;
}
}
{
eval {
$json->decode("-");
};
unless ($@ && Fn->index($@, "Invalid number", 0) >= 0) {
return 0;
}
}
{
my $test_case = ["1e.", "1e+.", "1.."];
for (my $i = 0; $i < @$test_case; ++$i) {
my $input = $test_case->[$i];
eval {
$json->decode($input);
};
unless ($@ && Fn->index($@, "Invalid number", 0) >= 0) {
return 0;
}
}
}
{
my $test_case = ["1.e", "1e+e", "1ee"];
for (my $i = 0; $i < @$test_case; ++$i) {
my $input = $test_case->[$i];
eval {
$json->decode($input);
};
unless ($@ && Fn->index($@, "Invalid number", 0) >= 0) {
return 0;
}
}
}
{
my $test_case = ["1.}"];
for (my $i = 0; $i < @$test_case; ++$i) {
my $input = $test_case->[$i];
eval {
$json->decode($input);
};
unless ($@ && Fn->index($@, "Invalid number", 0) >= 0) {
return 0;
}
}
}
{
my $test_case = ["1e}"];
for (my $i = 0; $i < @$test_case; ++$i) {
my $input = $test_case->[$i];
eval {
$json->decode($input);
};
unless ($@ && Fn->index($@, "Invalid number", 0) >= 0) {
return 0;
}
}
}
{
my $test_case = ["1e+}", "1e-}"];
for (my $i = 0; $i < @$test_case; ++$i) {
my $input = $test_case->[$i];
eval {
$json->decode($input);
};
unless ($@ && Fn->index($@, "Invalid number", 0) >= 0) {
return 0;
}
}
}
return 1;
}
static method encode_json_bool : int () {
my $json = JSON->new;
{
my $input = true;
my $got = $json->encode($input);
my $expected = "true";
unless ($got eq $expected) {
return 0;
}
}
{
my $input = false;
my $got = $json->encode($input);
my $expected = "false";
unless ($got eq $expected) {
return 0;
}
}
return 1;
}
static method decode_json_bool : int () {
my $json = JSON->new;
{
my $input = "true";
my $got = $json->decode($input);
my $expected = true;
unless (&_equals($got, $expected)) {
return 0;
}
}
{
my $input = "false";
my $got = $json->decode($input);
my $expected = false;
unless (&_equals($got, $expected)) {
return 0;
}
}
return 1;
}
static method encode_json_string : int () {
my $json = JSON->new;
{
my $input = "str";
my $got = $json->encode($input);
my $expected = "\"str\"";
unless ($got eq $expected) {
return 0;
}
}
# special characters
# '"'
{
my $input = "\"";
my $got = $json->encode($input);
my $expected = "\"\\\"\"";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '/'
{
my $input = "/";
my $got = $json->encode($input);
my $expected = "\"\\/\"";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\'
{
my $input = "\\";
my $got = $json->encode($input);
my $expected = "\"\\\\\"";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\n'
{
my $input = "\n";
my $got = $json->encode($input);
my $expected = "\"\\n\"";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\r'
{
my $input = "\r";
my $got = $json->encode($input);
my $expected = "\"\\r\"";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\t'
{
my $input = "\t";
my $got = $json->encode($input);
my $expected = "\"\\t\"";
unless (&_equals($got, $expected)) {
return 0;
}
}
return 1;
}
static method decode_json_string : int () {
my $json = JSON->new;
{
my $input = "\"str\"";
my $got = $json->decode($input);
my $expected = "str";
unless (&_equals($got, $expected)) {
return 0;
}
}
# special characters
# '"'
{
my $input = "\"\\\"\"";
my $got = $json->decode($input);
my $expected = "\"";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\'
{
my $input = "\"\\\\\"";
my $got = $json->decode($input);
my $expected = "\\";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\/'
{
my $input = "\"\\/\"";
my $got = $json->decode($input);
my $expected = "/";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '/'
{
my $input = "\"/\"";
my $got = $json->decode($input);
my $expected = "/";
unless (&_equals($got, $expected)) {
return 0;
}
}
# TODO
# '\b'
{
my $input = "\"\b\"";
my $got = $json->decode($input);
my $expected = "\x08";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\f'
{
my $input = "\"\\f\"";
my $got = $json->decode($input);
my $expected = "\f";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\n'
{
my $input = "\"\\n\"";
my $got = $json->decode($input);
my $expected = "\n";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\r'
{
my $input = "\"\\r\"";
my $got = $json->decode($input);
my $expected = "\r";
unless (&_equals($got, $expected)) {
return 0;
}
}
# '\t'
{
my $input = "\"\\t\"";
my $got = $json->decode($input);
my $expected = "\t";
unless (&_equals($got, $expected)) {
return 0;
}
}
# [TODO]
# control characters are not allowed
{
for (my $ch : byte = 0; $ch <= 31; ++$ch) {
eval {
$json->decode("\"" . (string)[(byte)$ch] . "\"");
};
unless ($@ && Fn->index($@, "Invalid string", 0) >= 0) {
return 0;
}
}
eval {
$json->decode("\"" . (string)[(byte)127 ] . "\"");
};
unless ($@ && Fn->index($@, "Invalid string", 0) >= 0) {
return 0;
}
}
return 1;
}
static method encode_json_null : int () {
my $json = JSON->new;
unless ($json->encode(undef) eq "null") {
return 0;
}
return 1;
}
static method decode_json_null : int () {
my $json = JSON->new;
unless ($json->decode("null") == undef) {
return 0;
}
return 1;
}
static method decode_json_invalid_json_data : int () {
my $json = JSON->new;
{
my $input = "";
eval { $json->decode($input); }
unless (Fn->contains($@, "The decoding of the \$json_data failed. Unexpected character \"\" at line 1 column 1.")) {
return 0;
}
}
{
my $input = "ppp";
eval { $json->decode($input); }
unless (Fn->contains($@, "The decoding of the \$json_data failed. Unexpected character \"p\" at line 1 column 1.")) {
return 0;
}
}
{
my $input = " ppp";
eval { $json->decode($input); }
unless (Fn->contains($@, "The decoding of the \$json_data failed. Unexpected character \"p\" at line 1 column 2.")) {
return 0;
}
}
{
my $input = "\n[ppp]";
eval { $json->decode($input); }
unless (Fn->contains($@, "The decoding of the \$json_data failed. Unexpected character \"p\" at line 2 column 2.")) {
return 0;
}
}
{
my $input = "[";
eval { $json->decode($input); }
unless (Fn->contains($@, "Unexpected character \"\" at line 1 column 2.")) {
return 0;
}
}
{
my $input = "{";
eval { $json->decode($input); }
unless (Fn->contains($@, "Unexpected character \"\" at line 1 column 2.")) {
return 0;
}
}
{
my $input = "to";
eval { $json->decode($input); }
unless (Fn->contains($@, "Expected token: \"true\" at line 1 column 1.")) {
return 0;
}
}
{
my $input = "fo";
eval { $json->decode($input); }
unless (Fn->contains($@, "Expected token: \"false\" at line 1 column 1.")) {
return 0;
}
}
return 1;
}
}