blob: ae6172c2e53b4cd8d2ecc057883ec7db740a1cb2 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <gtest/gtest.h>
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/container/map.h>
#include <tvm/ffi/extra/json.h>
#include <limits>
namespace {
using namespace tvm::ffi;
TEST(JSONWriter, BoolNull) {
// boolean value
EXPECT_EQ(json::Stringify(json::Value(true)), "true");
EXPECT_EQ(json::Stringify(json::Value(false)), "false");
EXPECT_EQ(json::Stringify(json::Value(nullptr)), "null");
}
TEST(JSONWriter, Integer) {
// positive integer
EXPECT_EQ(json::Stringify(json::Value(42)), "42");
// negative integer
EXPECT_EQ(json::Stringify(json::Value(-123)), "-123");
// zero
EXPECT_EQ(json::Stringify(json::Value(0)), "0");
// large positive integer
EXPECT_EQ(json::Stringify(json::Value(std::numeric_limits<int64_t>::max())),
"9223372036854775807");
// large negative integer
EXPECT_EQ(json::Stringify(json::Value(std::numeric_limits<int64_t>::min())),
"-9223372036854775808");
}
TEST(JSONWriter, Float) {
// regular float
EXPECT_EQ(json::Stringify(json::Value(2.5)), "2.5");
// integer-like float (should have .0 suffix)
EXPECT_EQ(json::Stringify(json::Value(5.0)), "5.0");
EXPECT_EQ(json::Stringify(json::Value(-10.0)), "-10.0");
// zero float
EXPECT_EQ(json::Stringify(json::Value(0.0)), "0.0");
// scientific notation for very small numbers
EXPECT_EQ(json::Stringify(json::Value(-7.89e-15)), "-7.89e-15");
// short scientific notation (shorter than fixed-point)
EXPECT_EQ(json::Stringify(json::Value(2e-8)), "2e-08");
// NaN
EXPECT_EQ(json::Stringify(json::Value(std::numeric_limits<double>::quiet_NaN())), "NaN");
// positive infinity
EXPECT_EQ(json::Stringify(json::Value(std::numeric_limits<double>::infinity())), "Infinity");
// negative infinity
EXPECT_EQ(json::Stringify(json::Value(-std::numeric_limits<double>::infinity())), "-Infinity");
}
TEST(JSONWriter, String) {
// simple string
EXPECT_EQ(json::Stringify(json::Value(String("hello"))), "\"hello\"");
// empty string
EXPECT_EQ(json::Stringify(json::Value(String(""))), "\"\"");
// string with escaped characters
EXPECT_EQ(json::Stringify(json::Value(String("\"quoted\""))), "\"\\\"quoted\\\"\"");
EXPECT_EQ(json::Stringify(json::Value(String("backslash\\"))), "\"backslash\\\\\"");
EXPECT_EQ(json::Stringify(json::Value(String("forward/slash"))), "\"forward\\/slash\"");
EXPECT_EQ(json::Stringify(json::Value(String("line\nbreak"))), "\"line\\nbreak\"");
EXPECT_EQ(json::Stringify(json::Value(String("tab\there"))), "\"tab\\there\"");
EXPECT_EQ(json::Stringify(json::Value(String("carriage\rreturn"))), "\"carriage\\rreturn\"");
// string with control character
EXPECT_EQ(json::Stringify(json::Value(String(std::string("\x01", 1) + "control"))),
"\"\\u0001control\"");
}
TEST(JSONWriter, Array) {
// empty array
json::Array empty_array;
EXPECT_EQ(json::Stringify(empty_array), "[]");
// single element array
json::Array single_array{42};
EXPECT_EQ(json::Stringify(single_array), "[42]");
// multiple elements array
json::Array multi_array{1, "hello", true};
EXPECT_EQ(json::Stringify(multi_array), "[1,\"hello\",true]");
// nested array
json::Array nested_array{json::Array{1, 2}, 3};
EXPECT_EQ(json::Stringify(nested_array), "[[1,2],3]");
}
TEST(JSONWriter, Object) {
// empty object
json::Object empty_object;
EXPECT_EQ(json::Stringify(empty_object), "{}");
// single key-value pair
json::Object single_object{{String("key"), String("value")}};
EXPECT_EQ(json::Stringify(single_object), "{\"key\":\"value\"}");
// multiple key-value pairs - insertion order preservation
json::Object multi_object{{"name", "Alice"}, {"age", 30}, {"active", true}, {"score", 95.5}};
EXPECT_EQ(json::Stringify(multi_object),
"{\"name\":\"Alice\",\"age\":30,\"active\":true,\"score\":95.5}");
}
TEST(JSONWriter, InsertionOrderPreservation) {
// test that objects preserve insertion order
json::Object ordered_object{
{"zebra", "last"}, {"alpha", "first"}, {"beta", "middle"}, {"gamma", 123}, {"delta", true}};
EXPECT_EQ(
json::Stringify(ordered_object),
"{\"zebra\":\"last\",\"alpha\":\"first\",\"beta\":\"middle\",\"gamma\":123,\"delta\":true}");
// test with indentation to verify order is preserved
std::string ordered_indented = json::Stringify(ordered_object, 2);
EXPECT_EQ(ordered_indented, String(R"({
"zebra": "last",
"alpha": "first",
"beta": "middle",
"gamma": 123,
"delta": true
})"));
// test nested objects also preserve order
json::Object nested_ordered{
{"outer1",
json::Object{{"inner_z", "z_value"}, {"inner_a", "a_value"}, {"inner_m", "m_value"}}},
{"outer2", json::Object{{"third", 3}, {"first", 1}, {"second", 2}}}};
std::string nested_ordered_indented = json::Stringify(nested_ordered, 2);
EXPECT_EQ(nested_ordered_indented, String(R"({
"outer1": {
"inner_z": "z_value",
"inner_a": "a_value",
"inner_m": "m_value"
},
"outer2": {
"third": 3,
"first": 1,
"second": 2
}
})"));
}
TEST(JSONWriter, NestedStructures) {
// object containing array
json::Object obj_with_array{{String("numbers"), json::Array{1, 2, 3}}};
EXPECT_EQ(json::Stringify(obj_with_array), "{\"numbers\":[1,2,3]}");
// array containing object
json::Array arr_with_obj{json::Object{{String("key"), String("value")}}};
EXPECT_EQ(json::Stringify(arr_with_obj), "[{\"key\":\"value\"}]");
// deeply nested structure
json::Object nested_obj{
{String("nested"), json::Array{json::Object{{String("deep"), String("value")}}}}};
EXPECT_EQ(json::Stringify(nested_obj), "{\"nested\":[{\"deep\":\"value\"}]}");
}
TEST(JSONWriter, Indentation) {
// test with indentation
json::Array arr{1, 2};
std::string indented = json::Stringify(arr, 2);
EXPECT_EQ(indented, String(R"([
1,
2
])"));
// object with indentation
json::Object obj{{"key", "value"}};
std::string indented_obj = json::Stringify(obj, 2);
EXPECT_EQ(indented_obj, String(R"({
"key": "value"
})"));
// complex nested structure with multiple data types
// keep double as .5 so output is deterministic as they exactly rounds to power of 2
json::Object complex_nested{
{"name", "test"},
{"count", 42},
{"price", 3.5},
{"active", true},
{"metadata", nullptr},
{"numbers", json::Array{1, 2, 3}},
{"config", json::Object{{"enabled", false},
{"timeout", 30.5},
{"tags", json::Array{"production", "critical", nullptr}}}},
{"matrix", json::Array{json::Array{1, 2}, json::Array{3.5, 4.5}, json::Array{"a", "b"}}}};
std::string complex_indented = json::Stringify(complex_nested, 2);
EXPECT_EQ(complex_indented, String(R"({
"name": "test",
"count": 42,
"price": 3.5,
"active": true,
"metadata": null,
"numbers": [
1,
2,
3
],
"config": {
"enabled": false,
"timeout": 30.5,
"tags": [
"production",
"critical",
null
]
},
"matrix": [
[
1,
2
],
[
3.5,
4.5
],
[
"a",
"b"
]
]
})"));
}
} // namespace