* Copyright (c) Huawei Technologies Co., Ltd. 2021-2024. All rights reserved.
* Description: json_value function test
*/
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "codegen/functions/stringfunctions.h"
#include "operator/execution_context.h"
#include "jni_mock.h"
#include "../util/test_util.h"
namespace omniruntime {
using namespace omniruntime::codegen::function;
using namespace omniruntime::op;
static void JsonValueTest(const std::string &jsonStr, const std::string &path,
const std::string &expectedResult, bool expectIsNull)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
const char *result = JsonValueRetNull(
contextPtr,
jsonStr.c_str(), static_cast<int32_t>(jsonStr.size()), false,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
&outIsNull, &outLen
);
if (expectIsNull) {
EXPECT_TRUE(outIsNull);
EXPECT_EQ(outLen, 0);
} else {
EXPECT_FALSE(outIsNull);
EXPECT_EQ(expectedResult, std::string(result, outLen));
}
delete context;
}
TEST(JsonValueTest, SimpleKeyAccess)
{
JsonValueTest(R"({"name":"John","age":30})", "$.name", "John", false);
JsonValueTest(R"({"name":"John","age":30})", "$.age", "30", false);
}
TEST(JsonValueTest, NestedKeyAccess)
{
JsonValueTest(R"({"user":{"name":"John","age":30}})", "$.user.name", "John", false);
JsonValueTest(R"({"user":{"name":"John","age":30}})", "$.user.age", "30", false);
}
TEST(JsonValueTest, ArrayIndexAccess)
{
JsonValueTest(R"([1,2,3,4,5])", "$[0]", "1", false);
JsonValueTest(R"([1,2,3,4,5])", "$[4]", "5", false);
JsonValueTest(R"({"items":[10,20,30]})", "$.items[1]", "20", false);
}
TEST(JsonValueTest, QuotedKeysWithSpecialChars)
{
JsonValueTest(R"({"key.with.dot":"value"})", "$['key.with.dot']", "value", false);
JsonValueTest(R"({"key-with-dash":"value"})", "$['key-with-dash']", "value", false);
JsonValueTest(R"({"key[0]":"value"})", "$['key[0]']", "value", false);
}
TEST(JsonValueTest, DoubleQuotedKeys)
{
JsonValueTest(R"({"key.with.dot":"value"})", "$[\"key.with.dot\"]", "value", false);
JsonValueTest(R"({"name":"test"})", "$[\"name\"]", "test", false);
}
TEST(JsonValueTest, DifferentValueTypes)
{
JsonValueTest(R"({"active":true})", "$.active", "true", false);
JsonValueTest(R"({"active":false})", "$.active", "false", false);
JsonValueTest(R"({"count":42})", "$.count", "42", false);
JsonValueTest(R"({"negative":-100})", "$.negative", "-100", false);
JsonValueTest(R"({"price":19.99})", "$.price", "19.99", false);
}
TEST(JsonValueTest, ComplexNestedStructures)
{
std::string json = R"({
"company": {
"name": "TechCorp",
"employees": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
}
})";
JsonValueTest(json, "$.company.name", "TechCorp", false);
JsonValueTest(json, "$.company.employees[0].name", "Alice", false);
JsonValueTest(json, "$.company.employees[1].id", "2", false);
}
TEST(JsonValueTest, NullAndMissingValues)
{
JsonValueTest(R"({"value":null})", "$.value", "", true);
JsonValueTest(R"({"name":"John"})", "$.age", "", true);
JsonValueTest(R"({"user":{"name":"John"}})", "$.user.email", "", true);
JsonValueTest(R"([1,2,3])", "$[5]", "", true);
}
TEST(JsonValueTest, InputNullHandling)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
std::string json = R"({"name":"John"})";
std::string path = "$.name";
const char *result = JsonValueRetNull(
contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), true,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
&outIsNull, &outLen
);
EXPECT_TRUE(outIsNull);
result = JsonValueRetNull(
contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), false,
path.c_str(), 0, static_cast<int32_t>(path.size()), true,
&outIsNull, &outLen
);
EXPECT_TRUE(outIsNull);
delete context;
}
TEST(JsonValueTest, InvalidPaths)
{
JsonValueTest(R"({"name":"John"})", "name", "", true);
JsonValueTest(R"({"name":"John"})", "", "", true);
}
TEST(JsonValueTest, EscapedCharactersInJson)
{
JsonValueTest(R"({"message":"Hello \"World\""})", "$.message", R"(Hello "World")", false);
JsonValueTest(R"({"path":"C:\\Users\\test"})", "$.path", R"(C:\Users\test)", false);
}
TEST(JsonValueTest, ComplexTypesReturnNull)
{
JsonValueTest(R"({"data":{"x":1,"y":2}})", "$.data", "", true);
JsonValueTest(R"({"items":[1,2,3]})", "$.items", "", true);
}
TEST(JsonValueTest, MixedNotation)
{
JsonValueTest(R"({"users":[{"name":"Alice"},{"name":"Bob"}]})",
"$.users[0].name", "Alice", false);
JsonValueTest(R"({"data":{"matrix":[[1,2],[3,4]]}})",
"$.data.matrix[1][0]", "3", false);
}
TEST(JsonValueTest, LargeJsonPerformance)
{
std::string largeJson = R"({
"level1": {
"level2": {
"level3": {
"level4": {
"level5": {
"value": "deepvalue"
}
}
}
}
}
})";
JsonValueTest(largeJson, "$.level1.level2.level3.level4.level5.value", "deepvalue", false);
}
TEST(JsonValueTest, CacheResetsAcrossJsonSizes)
{
std::string largeJson = R"({"payload":")" + std::string(8192, 'x') + R"(", "flag": true})";
JsonValueTest(largeJson, "$.payload", std::string(8192, 'x'), false);
JsonValueTest(R"({"payload":"small","flag":false})", "$.payload", "small", false);
}
TEST(JsonValueTest, PathsWithWhitespace)
{
JsonValueTest(R"({"name":"John"})", "$. name", "", true);
}
TEST(JsonValueTest, ExtendedOnEmptyNull)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
std::string json = R"({"name":"John"})";
std::string path = "$.age";
const char *result = JsonValueExtended(
contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), false,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
0, nullptr, 0, true,
0, nullptr, 0, true,
&outIsNull, &outLen
);
EXPECT_TRUE(outIsNull);
EXPECT_EQ(outLen, 0);
delete context;
}
TEST(JsonValueTest, ExtendedOnEmptyDefault)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
std::string json = R"({"name":"John"})";
std::string path = "$.age";
std::string defaultValue = "unknown";
const char *result = JsonValueExtended(
contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), false,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
2, defaultValue.c_str(), static_cast<int32_t>(defaultValue.size()), false,
0, nullptr, 0, true,
&outIsNull, &outLen
);
EXPECT_FALSE(outIsNull);
EXPECT_EQ("unknown", std::string(result, outLen));
delete context;
}
TEST(JsonValueTest, ExtendedComplexTypesUseEmptyBehavior)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
std::string json = R"({"data":{"x":1,"y":2}})";
std::string path = "$.data";
std::string defaultValue = "missing";
const char *result = JsonValueExtended(
contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), false,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
2, defaultValue.c_str(), static_cast<int32_t>(defaultValue.size()), false,
0, nullptr, 0, true,
&outIsNull, &outLen
);
EXPECT_FALSE(outIsNull);
EXPECT_EQ("missing", std::string(result, outLen));
delete context;
}
TEST(JsonValueTest, ExtendedOnErrorNull)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
std::string json = "invalid json";
std::string path = "$.name";
const char *result = JsonValueExtended(
contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), false,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
0, nullptr, 0, true,
0, nullptr, 0, true,
&outIsNull, &outLen
);
EXPECT_TRUE(outIsNull);
EXPECT_EQ(outLen, 0);
delete context;
}
TEST(JsonValueTest, ExtendedOnErrorDefault)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
std::string json = "invalid json";
std::string path = "$.name";
std::string defaultValue = "error_default";
const char *result = JsonValueExtended(
contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), false,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
0, nullptr, 0, true,
2, defaultValue.c_str(), static_cast<int32_t>(defaultValue.size()), false,
&outIsNull, &outLen
);
EXPECT_FALSE(outIsNull);
EXPECT_EQ("error_default", std::string(result, outLen));
delete context;
}
TEST(JsonValueTest, ExtendedNullInput)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
std::string json = R"({"name":"John"})";
std::string path = "$.name";
std::string defaultValue = "null_input_default";
const char *result = JsonValueExtended(
contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), true,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
0, nullptr, 0, true,
2, defaultValue.c_str(), static_cast<int32_t>(defaultValue.size()), false,
&outIsNull, &outLen
);
EXPECT_FALSE(outIsNull);
EXPECT_EQ("null_input_default", std::string(result, outLen));
delete context;
}
TEST(JsonValueTest, WrappedExtendedOnEmptyDefault)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
std::string json = R"({"name":"John"})";
std::string path = "$.age";
std::string defaultValue = "unknown";
const char *result = JsonValueWithBehaviors(contextPtr,
json.c_str(), static_cast<int32_t>(json.size()), false,
path.c_str(), 0, static_cast<int32_t>(path.size()), false,
2, false, defaultValue.c_str(), static_cast<int32_t>(defaultValue.size()), false,
0, false, nullptr, 0, true, &outIsNull, &outLen);
EXPECT_FALSE(outIsNull);
EXPECT_EQ("unknown", std::string(result, outLen));
delete context;
}
static void JsonSplitScalarTest(const std::string &jsonStr,
const std::string &expectedResult, bool expectIsNull)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
const char *result = JsonSplitScalar(
contextPtr,
jsonStr.c_str(), static_cast<int32_t>(jsonStr.size()), false,
&outIsNull, &outLen
);
if (expectIsNull) {
EXPECT_TRUE(outIsNull);
EXPECT_EQ(outLen, 0);
} else {
EXPECT_FALSE(outIsNull);
EXPECT_EQ(expectedResult, std::string(result, outLen));
}
delete context;
}
static void JsonSplitScalarCharTest(const std::string &jsonStr, int32_t width,
const std::string &expectedResult, bool expectIsNull)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
const char *result = JsonSplitScalarChar(
contextPtr,
jsonStr.c_str(), width, static_cast<int32_t>(jsonStr.size()), false,
&outIsNull, &outLen
);
if (expectIsNull) {
EXPECT_TRUE(outIsNull);
EXPECT_EQ(outLen, 0);
} else {
EXPECT_FALSE(outIsNull);
EXPECT_EQ(expectedResult, std::string(result, outLen));
}
delete context;
}
TEST(JsonSplitScalarTest, SimpleArray)
{
JsonSplitScalarTest(R"(["a","b","c"])", "a\r\nb\r\nc", false);
}
TEST(JsonSplitScalarTest, CharInput)
{
JsonSplitScalarCharTest(R"(["'dIPFMXtJL"])", 1, "'dIPFMXtJL", false);
}
TEST(JsonSplitScalarTest, NumericArray)
{
JsonSplitScalarTest(R"([1,2,3,4,5])", "1\r\n2\r\n3\r\n4\r\n5", false);
}
TEST(JsonSplitScalarTest, FloatingPointFormatting)
{
JsonSplitScalarTest(R"([1.2,3.14,-2.5])", "1.2\r\n3.14\r\n-2.5", false);
}
TEST(JsonSplitScalarTest, ScientificNotationFormatting)
{
JsonSplitScalarTest(R"([1.171815966525443e+77])", "1.171815966525443E+77", false);
}
TEST(JsonSplitScalarTest, MixedArray)
{
JsonSplitScalarTest(R"(["string",123,true,null])", "string\r\n123\r\ntrue\r\nnull", false);
}
TEST(JsonSplitScalarTest, NestedArray)
{
JsonSplitScalarTest(R"([[1,2],[3,4]])", "[1,2]\r\n[3,4]", false);
}
TEST(JsonSplitScalarTest, ObjectArray)
{
JsonSplitScalarTest(R"([{"name":"Alice"},{"name":"Bob"}])", "{\"name\":\"Alice\"}\r\n{\"name\":\"Bob\"}", false);
}
TEST(JsonSplitScalarTest, SingleQuotedStringArray)
{
JsonSplitScalarTest(R"(['apple','banana'])", "apple\r\nbanana", false);
}
TEST(JsonSplitScalarTest, SingleQuotedJsonStringArray)
{
JsonSplitScalarTest(R"(['{"id":865,"name":"Alice"}'])", "{\"id\":865,\"name\":\"Alice\"}", false);
}
TEST(JsonSplitScalarTest, SingleElementArray)
{
JsonSplitScalarTest(R"(["only"])", "only", false);
}
TEST(JsonSplitScalarTest, EmptyArray)
{
JsonSplitScalarTest(R"([])", "", false);
}
TEST(JsonSplitScalarTest, NotArray)
{
JsonSplitScalarTest(R"({"key":"value"})", "", true);
JsonSplitScalarTest(R"("string")", "", true);
JsonSplitScalarTest(R"(123)", "", true);
}
TEST(JsonSplitScalarTest, NullInput)
{
auto context = new ExecutionContext();
int64_t contextPtr = reinterpret_cast<int64_t>(context);
bool outIsNull = false;
int32_t outLen = 0;
const char *result = JsonSplitScalar(
contextPtr,
nullptr, 0, true,
&outIsNull, &outLen
);
EXPECT_TRUE(outIsNull);
EXPECT_EQ(outLen, 0);
delete context;
}
TEST(JsonSplitScalarTest, InvalidJson)
{
JsonSplitScalarTest("invalid json", "", true);
JsonSplitScalarTest("[1,2,", "", true);
}
}