blob: b055651ddf239f4dc4b944bec58717c34bc31785 [file] [log] [blame]
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/types.h>
#include <stdbool.h>
#include <zephyr/ztest.h>
#include <zephyr/data/json.h>
struct test_nested {
int nested_int;
bool nested_bool;
const char *nested_string;
};
struct test_struct {
const char *some_string;
int some_int;
bool some_bool;
struct test_nested some_nested_struct;
int some_array[16];
size_t some_array_len;
bool another_bxxl; /* JSON field: "another_b!@l" */
bool if_; /* JSON: "if" */
int another_array[10]; /* JSON: "another-array" */
size_t another_array_len;
struct test_nested xnother_nexx; /* JSON: "4nother_ne$+" */
struct test_nested nested_obj_array[2];
size_t obj_array_len;
};
struct elt {
const char *name;
int height;
};
struct obj_array {
struct elt elements[10];
size_t num_elements;
};
struct test_int_limits {
int int_max;
int int_cero;
int int_min;
};
static const struct json_obj_descr nested_descr[] = {
JSON_OBJ_DESCR_PRIM(struct test_nested, nested_int, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_nested, nested_bool, JSON_TOK_TRUE),
JSON_OBJ_DESCR_PRIM(struct test_nested, nested_string,
JSON_TOK_STRING),
};
static const struct json_obj_descr test_descr[] = {
JSON_OBJ_DESCR_PRIM(struct test_struct, some_string, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct test_struct, some_int, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_struct, some_bool, JSON_TOK_TRUE),
JSON_OBJ_DESCR_OBJECT(struct test_struct, some_nested_struct,
nested_descr),
JSON_OBJ_DESCR_ARRAY(struct test_struct, some_array,
16, some_array_len, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM_NAMED(struct test_struct, "another_b!@l",
another_bxxl, JSON_TOK_TRUE),
JSON_OBJ_DESCR_PRIM_NAMED(struct test_struct, "if",
if_, JSON_TOK_TRUE),
JSON_OBJ_DESCR_ARRAY_NAMED(struct test_struct, "another-array",
another_array, 10, another_array_len,
JSON_TOK_NUMBER),
JSON_OBJ_DESCR_OBJECT_NAMED(struct test_struct, "4nother_ne$+",
xnother_nexx, nested_descr),
JSON_OBJ_DESCR_OBJ_ARRAY(struct test_struct, nested_obj_array, 2,
obj_array_len, nested_descr, ARRAY_SIZE(nested_descr)),
};
static const struct json_obj_descr elt_descr[] = {
JSON_OBJ_DESCR_PRIM(struct elt, name, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct elt, height, JSON_TOK_NUMBER),
};
static const struct json_obj_descr obj_array_descr[] = {
JSON_OBJ_DESCR_OBJ_ARRAY(struct obj_array, elements, 10, num_elements,
elt_descr, ARRAY_SIZE(elt_descr)),
};
static const struct json_obj_descr obj_limits_descr[] = {
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int_max, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int_cero, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int_min, JSON_TOK_NUMBER),
};
struct array {
struct elt objects;
};
struct obj_array_array {
struct array objects_array[4];
size_t objects_array_len;
};
static const struct json_obj_descr array_descr[] = {
JSON_OBJ_DESCR_OBJECT(struct array, objects, elt_descr),
};
static const struct json_obj_descr array_array_descr[] = {
JSON_OBJ_DESCR_ARRAY_ARRAY(struct obj_array_array, objects_array, 4,
objects_array_len, array_descr,
ARRAY_SIZE(array_descr)),
};
struct obj_array_2dim {
struct obj_array objects_array_array[3];
size_t objects_array_array_len;
};
static const struct json_obj_descr array_2dim_descr[] = {
JSON_OBJ_DESCR_ARRAY_ARRAY(struct obj_array_2dim, objects_array_array, 3,
objects_array_array_len, obj_array_descr,
ARRAY_SIZE(obj_array_descr)),
};
struct obj_array_2dim_extra {
const char *name;
int val;
struct obj_array_2dim obj_array_2dim;
};
static const struct json_obj_descr array_2dim_extra_descr[] = {
JSON_OBJ_DESCR_PRIM(struct obj_array_2dim_extra, name, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct obj_array_2dim_extra, val, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_ARRAY_ARRAY(struct obj_array_2dim_extra, obj_array_2dim, 3,
obj_array_2dim.objects_array_array_len, obj_array_descr,
ARRAY_SIZE(obj_array_descr)),
};
static const struct json_obj_descr array_2dim_extra_named_descr[] = {
JSON_OBJ_DESCR_PRIM(struct obj_array_2dim_extra, name, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct obj_array_2dim_extra, val, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_ARRAY_ARRAY_NAMED(struct obj_array_2dim_extra, data, obj_array_2dim, 3,
obj_array_2dim.objects_array_array_len, obj_array_descr,
ARRAY_SIZE(obj_array_descr)),
};
ZTEST(lib_json_test, test_json_encoding)
{
struct test_struct ts = {
.some_string = "zephyr 123\uABCD",
.some_int = 42,
.some_bool = true,
.some_nested_struct = {
.nested_int = -1234,
.nested_bool = false,
.nested_string = "this should be escaped: \t"
},
.some_array[0] = 1,
.some_array[1] = 4,
.some_array[2] = 8,
.some_array[3] = 16,
.some_array[4] = 32,
.some_array_len = 5,
.another_bxxl = true,
.if_ = false,
.another_array[0] = 2,
.another_array[1] = 3,
.another_array[2] = 5,
.another_array[3] = 7,
.another_array_len = 4,
.xnother_nexx = {
.nested_int = 1234,
.nested_bool = true,
.nested_string = "no escape necessary",
},
.nested_obj_array = {
{1, true, "true"},
{0, false, "false"}
},
.obj_array_len = 2
};
char encoded[] = "{\"some_string\":\"zephyr 123\uABCD\","
"\"some_int\":42,\"some_bool\":true,"
"\"some_nested_struct\":{\"nested_int\":-1234,"
"\"nested_bool\":false,\"nested_string\":"
"\"this should be escaped: \\t\"},"
"\"some_array\":[1,4,8,16,32],"
"\"another_b!@l\":true,"
"\"if\":false,"
"\"another-array\":[2,3,5,7],"
"\"4nother_ne$+\":{\"nested_int\":1234,"
"\"nested_bool\":true,"
"\"nested_string\":\"no escape necessary\"},"
"\"nested_obj_array\":["
"{\"nested_int\":1,\"nested_bool\":true,\"nested_string\":\"true\"},"
"{\"nested_int\":0,\"nested_bool\":false,\"nested_string\":\"false\"}]"
"}";
char buffer[sizeof(encoded)];
int ret;
ssize_t len;
len = json_calc_encoded_len(test_descr, ARRAY_SIZE(test_descr), &ts);
zassert_equal(len, strlen(encoded), "encoded size mismatch");
ret = json_obj_encode_buf(test_descr, ARRAY_SIZE(test_descr),
&ts, buffer, sizeof(buffer));
zassert_equal(ret, 0, "Encoding function failed");
ret = strncmp(buffer, encoded, sizeof(encoded) - 1);
zassert_equal(ret, 0, "Encoded contents not consistent");
}
ZTEST(lib_json_test, test_json_decoding)
{
struct test_struct ts;
char encoded[] = "{\"some_string\":\"zephyr 123\\uABCD456\","
"\"some_int\":\t42\n,"
"\"some_bool\":true \t "
"\n"
"\r ,"
"\"some_nested_struct\":{ "
"\"nested_int\":-1234,\n\n"
"\"nested_bool\":false,\t"
"\"nested_string\":\"this should be escaped: \\t\","
"\"extra_nested_array\":[0,-1]},"
"\"extra_struct\":{\"nested_bool\":false},"
"\"extra_bool\":true,"
"\"some_array\":[11,22, 33,\t45,\n299],"
"\"another_b!@l\":true,"
"\"if\":false,"
"\"another-array\":[2,3,5,7],"
"\"4nother_ne$+\":{\"nested_int\":1234,"
"\"nested_bool\":true,"
"\"nested_string\":\"no escape necessary\"},"
"\"nested_obj_array\":["
"{\"nested_int\":1,\"nested_bool\":true,\"nested_string\":\"true\"},"
"{\"nested_int\":0,\"nested_bool\":false,\"nested_string\":\"false\"}]"
"}\n";
const int expected_array[] = { 11, 22, 33, 45, 299 };
const int expected_other_array[] = { 2, 3, 5, 7 };
int ret;
ret = json_obj_parse(encoded, sizeof(encoded) - 1, test_descr,
ARRAY_SIZE(test_descr), &ts);
zassert_equal(ret, (1 << ARRAY_SIZE(test_descr)) - 1,
"Not all fields decoded correctly");
zassert_true(!strcmp(ts.some_string, "zephyr 123\\uABCD456"),
"String not decoded correctly");
zassert_equal(ts.some_int, 42, "Positive integer not decoded correctly");
zassert_equal(ts.some_bool, true, "Boolean not decoded correctly");
zassert_equal(ts.some_nested_struct.nested_int, -1234,
"Nested negative integer not decoded correctly");
zassert_equal(ts.some_nested_struct.nested_bool, false,
"Nested boolean value not decoded correctly");
zassert_true(!strcmp(ts.some_nested_struct.nested_string,
"this should be escaped: \\t"),
"Nested string not decoded correctly");
zassert_equal(ts.some_array_len, 5,
"Array doesn't have correct number of items");
zassert_true(!memcmp(ts.some_array, expected_array,
sizeof(expected_array)),
"Array not decoded with expected values");
zassert_true(ts.another_bxxl,
"Named boolean (special chars) not decoded correctly");
zassert_false(ts.if_,
"Named boolean (reserved word) not decoded correctly");
zassert_equal(ts.another_array_len, 4,
"Named array does not have correct number of items");
zassert_true(!memcmp(ts.another_array, expected_other_array,
sizeof(expected_other_array)),
"Decoded named array not with expected values");
zassert_equal(ts.xnother_nexx.nested_int, 1234,
"Named nested integer not decoded correctly");
zassert_equal(ts.xnother_nexx.nested_bool, true,
"Named nested boolean not decoded correctly");
zassert_true(!strcmp(ts.xnother_nexx.nested_string,
"no escape necessary"),
"Named nested string not decoded correctly");
zassert_equal(ts.obj_array_len, 2,
"Array of objects does not have correct number of items");
zassert_equal(ts.nested_obj_array[0].nested_int, 1,
"Integer in first object array element not decoded correctly");
zassert_equal(ts.nested_obj_array[0].nested_bool, true,
"Boolean value in first object array element not decoded correctly");
zassert_true(!strcmp(ts.nested_obj_array[0].nested_string, "true"),
"String in first object array element not decoded correctly");
zassert_equal(ts.nested_obj_array[1].nested_int, 0,
"Integer in second object array element not decoded correctly");
zassert_equal(ts.nested_obj_array[1].nested_bool, false,
"Boolean value in second object array element not decoded correctly");
zassert_true(!strcmp(ts.nested_obj_array[1].nested_string, "false"),
"String in second object array element not decoded correctly");
}
ZTEST(lib_json_test, test_json_limits)
{
int ret = 0;
char encoded[] = "{\"int_max\":2147483647,"
"\"int_cero\":0,"
"\"int_min\":-2147483648"
"}";
struct test_int_limits limits = {
.int_max = INT_MAX,
.int_cero = 0,
.int_min = INT_MIN,
};
char buffer[sizeof(encoded)];
struct test_int_limits limits_decoded = {0};
ret = json_obj_encode_buf(obj_limits_descr, ARRAY_SIZE(obj_limits_descr),
&limits, buffer, sizeof(buffer));
ret = json_obj_parse(encoded, sizeof(encoded) - 1, obj_limits_descr,
ARRAY_SIZE(obj_limits_descr), &limits_decoded);
zassert_true(!strcmp(encoded, buffer), "Integer limits not encoded correctly");
zassert_true(!memcmp(&limits, &limits_decoded, sizeof(limits)),
"Integer limits not decoded correctly");
}
ZTEST(lib_json_test, test_json_encoding_array_array)
{
struct obj_array_array obj_array_array_ts = {
.objects_array = {
[0] = { { .name = "Sim\303\263n Bol\303\255var", .height = 168 } },
[1] = { { .name = "Pel\303\251", .height = 173 } },
[2] = { { .name = "Usain Bolt", .height = 195 } },
},
.objects_array_len = 3,
};
char encoded[] = "{\"objects_array\":["
"{\"name\":\"Sim\303\263n Bol\303\255var\",\"height\":168},"
"{\"name\":\"Pel\303\251\",\"height\":173},"
"{\"name\":\"Usain Bolt\",\"height\":195}"
"]}";
char buffer[sizeof(encoded)];
int ret;
ret = json_obj_encode_buf(array_array_descr, ARRAY_SIZE(array_array_descr),
&obj_array_array_ts, buffer, sizeof(buffer));
zassert_equal(ret, 0, "Encoding array returned error");
zassert_true(!strcmp(buffer, encoded),
"Encoded array of objects is not consistent");
}
ZTEST(lib_json_test, test_json_decoding_array_array)
{
int ret;
struct obj_array_array obj_array_array_ts;
char encoded[] = "{\"objects_array\":["
"{\"height\":168,\"name\":\"Sim\303\263n Bol\303\255var\"},"
"{\"height\":173,\"name\":\"Pel\303\251\"},"
"{\"height\":195,\"name\":\"Usain Bolt\"}]"
"}";
ret = json_obj_parse(encoded, sizeof(encoded),
array_array_descr,
ARRAY_SIZE(array_array_descr),
&obj_array_array_ts);
zassert_equal(ret, 1, "Encoding array of objects returned error");
zassert_equal(obj_array_array_ts.objects_array_len, 3,
"Array doesn't have correct number of items");
zassert_true(!strcmp(obj_array_array_ts.objects_array[0].objects.name,
"Sim\303\263n Bol\303\255var"), "String not decoded correctly");
zassert_equal(obj_array_array_ts.objects_array[0].objects.height, 168,
"Sim\303\263n Bol\303\255var height not decoded correctly");
zassert_true(!strcmp(obj_array_array_ts.objects_array[1].objects.name,
"Pel\303\251"), "String not decoded correctly");
zassert_equal(obj_array_array_ts.objects_array[1].objects.height, 173,
"Pel\303\251 height not decoded correctly");
zassert_true(!strcmp(obj_array_array_ts.objects_array[2].objects.name,
"Usain Bolt"), "String not decoded correctly");
zassert_equal(obj_array_array_ts.objects_array[2].objects.height, 195,
"Usain Bolt height not decoded correctly");
}
ZTEST(lib_json_test, test_json_obj_arr_encoding)
{
struct obj_array oa = {
.elements = {
[0] = { .name = "Sim\303\263n Bol\303\255var", .height = 168 },
[1] = { .name = "Muggsy Bogues", .height = 160 },
[2] = { .name = "Pel\303\251", .height = 173 },
[3] = { .name = "Hakeem Olajuwon", .height = 213 },
[4] = { .name = "Alex Honnold", .height = 180 },
[5] = { .name = "Hazel Findlay", .height = 157 },
[6] = { .name = "Daila Ojeda", .height = 158 },
[7] = { .name = "Albert Einstein", .height = 172 },
[8] = { .name = "Usain Bolt", .height = 195 },
[9] = { .name = "Paavo Nurmi", .height = 174 },
},
.num_elements = 10,
};
char encoded[] = "{\"elements\":["
"{\"name\":\"Sim\303\263n Bol\303\255var\",\"height\":168},"
"{\"name\":\"Muggsy Bogues\",\"height\":160},"
"{\"name\":\"Pel\303\251\",\"height\":173},"
"{\"name\":\"Hakeem Olajuwon\",\"height\":213},"
"{\"name\":\"Alex Honnold\",\"height\":180},"
"{\"name\":\"Hazel Findlay\",\"height\":157},"
"{\"name\":\"Daila Ojeda\",\"height\":158},"
"{\"name\":\"Albert Einstein\",\"height\":172},"
"{\"name\":\"Usain Bolt\",\"height\":195},"
"{\"name\":\"Paavo Nurmi\",\"height\":174}"
"]}";
char buffer[sizeof(encoded)];
int ret;
ret = json_obj_encode_buf(obj_array_descr, ARRAY_SIZE(obj_array_descr),
&oa, buffer, sizeof(buffer));
zassert_equal(ret, 0, "Encoding array of object returned error");
zassert_true(!strcmp(buffer, encoded),
"Encoded array of objects is not consistent");
}
ZTEST(lib_json_test, test_json_arr_obj_decoding)
{
int ret;
struct obj_array obj_array_array_ts;
char encoded[] = "[{\"height\":168,\"name\":\"Sim\303\263n Bol\303\255var\"},"
"{\"height\":173,\"name\":\"Pel\303\251\"},"
"{\"height\":195,\"name\":\"Usain Bolt\"}"
"]";
ret = json_arr_parse(encoded, sizeof(encoded),
obj_array_descr,
&obj_array_array_ts);
zassert_equal(ret, 0, "Encoding array of objects returned error %d", ret);
zassert_equal(obj_array_array_ts.num_elements, 3,
"Array doesn't have correct number of items");
zassert_true(!strcmp(obj_array_array_ts.elements[0].name,
"Sim\303\263n Bol\303\255var"), "String not decoded correctly");
zassert_equal(obj_array_array_ts.elements[0].height, 168,
"Sim\303\263n Bol\303\255var height not decoded correctly");
zassert_true(!strcmp(obj_array_array_ts.elements[1].name,
"Pel\303\251"), "String not decoded correctly");
zassert_equal(obj_array_array_ts.elements[1].height, 173,
"Pel\303\251 height not decoded correctly");
zassert_true(!strcmp(obj_array_array_ts.elements[2].name,
"Usain Bolt"), "String not decoded correctly");
zassert_equal(obj_array_array_ts.elements[2].height, 195,
"Usain Bolt height not decoded correctly");
}
ZTEST(lib_json_test, test_json_arr_obj_encoding)
{
struct obj_array oa = {
.elements = {
[0] = { .name = "Sim\303\263n Bol\303\255var", .height = 168 },
[1] = { .name = "Muggsy Bogues", .height = 160 },
[2] = { .name = "Pel\303\251", .height = 173 },
[3] = { .name = "Hakeem Olajuwon", .height = 213 },
[4] = { .name = "Alex Honnold", .height = 180 },
[5] = { .name = "Hazel Findlay", .height = 157 },
[6] = { .name = "Daila Ojeda", .height = 158 },
[7] = { .name = "Albert Einstein", .height = 172 },
[8] = { .name = "Usain Bolt", .height = 195 },
[9] = { .name = "Paavo Nurmi", .height = 174 },
},
.num_elements = 10,
};
char encoded[] = "["
"{\"name\":\"Sim\303\263n Bol\303\255var\",\"height\":168},"
"{\"name\":\"Muggsy Bogues\",\"height\":160},"
"{\"name\":\"Pel\303\251\",\"height\":173},"
"{\"name\":\"Hakeem Olajuwon\",\"height\":213},"
"{\"name\":\"Alex Honnold\",\"height\":180},"
"{\"name\":\"Hazel Findlay\",\"height\":157},"
"{\"name\":\"Daila Ojeda\",\"height\":158},"
"{\"name\":\"Albert Einstein\",\"height\":172},"
"{\"name\":\"Usain Bolt\",\"height\":195},"
"{\"name\":\"Paavo Nurmi\",\"height\":174}"
"]";
char buffer[sizeof(encoded)];
int ret;
ssize_t len;
len = json_calc_encoded_arr_len(obj_array_descr, &oa);
zassert_equal(len, strlen(encoded), "encoded size mismatch");
ret = json_arr_encode_buf(obj_array_descr, &oa, buffer, sizeof(buffer));
zassert_equal(ret, 0, "Encoding array of object returned error %d", ret);
zassert_true(!strcmp(buffer, encoded),
"Encoded array of objects is not consistent");
}
ZTEST(lib_json_test, test_json_obj_arr_decoding)
{
struct obj_array oa;
char encoded[] = "{\"elements\":["
"{\"name\":\"Sim\303\263n Bol\303\255var\",\"height\":168},"
"{\"name\":\"Muggsy Bogues\",\"height\":160},"
"{\"name\":\"Pel\303\251\",\"height\":173},"
"{\"name\":\"Hakeem Olajuwon\",\"height\":213},"
"{\"name\":\"Alex Honnold\",\"height\":180},"
"{\"name\":\"Hazel Findlay\",\"height\":157},"
"{\"name\":\"Daila Ojeda\",\"height\":158},"
"{\"name\":\"Albert Einstein\",\"height\":172},"
"{\"name\":\"Usain Bolt\",\"height\":195},"
"{\"name\":\"Paavo Nurmi\",\"height\":174}"
"]}";
const struct obj_array expected = {
.elements = {
[0] = { .name = "Sim\303\263n Bol\303\255var", .height = 168 },
[1] = { .name = "Muggsy Bogues", .height = 160 },
[2] = { .name = "Pel\303\251", .height = 173 },
[3] = { .name = "Hakeem Olajuwon", .height = 213 },
[4] = { .name = "Alex Honnold", .height = 180 },
[5] = { .name = "Hazel Findlay", .height = 157 },
[6] = { .name = "Daila Ojeda", .height = 158 },
[7] = { .name = "Albert Einstein", .height = 172 },
[8] = { .name = "Usain Bolt", .height = 195 },
[9] = { .name = "Paavo Nurmi", .height = 174 },
},
.num_elements = 10,
};
int ret;
ret = json_obj_parse(encoded, sizeof(encoded) - 1, obj_array_descr,
ARRAY_SIZE(obj_array_descr), &oa);
zassert_equal(ret, (1 << ARRAY_SIZE(obj_array_descr)) - 1,
"Array of object fields not decoded correctly");
zassert_equal(oa.num_elements, 10,
"Number of object fields not decoded correctly");
for (int i = 0; i < expected.num_elements; i++) {
zassert_true(!strcmp(oa.elements[i].name,
expected.elements[i].name),
"Element %d name not decoded correctly", i);
zassert_equal(oa.elements[i].height,
expected.elements[i].height,
"Element %d height not decoded correctly", i);
}
}
ZTEST(lib_json_test, test_json_2dim_arr_obj_encoding)
{
struct obj_array_2dim obj_array_array_ts = {
.objects_array_array = {
[0] = {
.elements = {
[0] = {
.name = "Sim\303\263n Bol\303\255var",
.height = 168
},
[1] = {
.name = "Pel\303\251",
.height = 173
},
[2] = {
.name = "Usain Bolt",
.height = 195
},
},
.num_elements = 3
},
[1] = {
.elements = {
[0] = {
.name = "Muggsy Bogues",
.height = 160
},
[1] = {
.name = "Hakeem Olajuwon",
.height = 213
},
},
.num_elements = 2
},
[2] = {
.elements = {
[0] = {
.name = "Alex Honnold",
.height = 180
},
[1] = {
.name = "Hazel Findlay",
.height = 157
},
[2] = {
.name = "Daila Ojeda",
.height = 158
},
[3] = {
.name = "Albert Einstein",
.height = 172
},
},
.num_elements = 4
},
},
.objects_array_array_len = 3,
};
char encoded[] = "{\"objects_array_array\":["
"[{\"name\":\"Sim\303\263n Bol\303\255var\",\"height\":168},"
"{\"name\":\"Pel\303\251\",\"height\":173},"
"{\"name\":\"Usain Bolt\",\"height\":195}],"
"[{\"name\":\"Muggsy Bogues\",\"height\":160},"
"{\"name\":\"Hakeem Olajuwon\",\"height\":213}],"
"[{\"name\":\"Alex Honnold\",\"height\":180},"
"{\"name\":\"Hazel Findlay\",\"height\":157},"
"{\"name\":\"Daila Ojeda\",\"height\":158},"
"{\"name\":\"Albert Einstein\",\"height\":172}]"
"]}";
char buffer[sizeof(encoded)];
int ret;
ret = json_obj_encode_buf(array_2dim_descr, ARRAY_SIZE(array_2dim_descr),
&obj_array_array_ts, buffer, sizeof(buffer));
zassert_equal(ret, 0, "Encoding two-dimensional array returned error");
zassert_true(!strcmp(buffer, encoded),
"Encoded two-dimensional array is not consistent");
}
ZTEST(lib_json_test, test_json_2dim_arr_extra_obj_encoding)
{
struct obj_array_2dim_extra obj_array_2dim_extra_ts = {
.name = "Paavo Nurmi",
.val = 123,
.obj_array_2dim.objects_array_array = {
[0] = {
.elements = {
[0] = {
.name = "Sim\303\263n Bol\303\255var",
.height = 168
},
[1] = {
.name = "Pel\303\251",
.height = 173
},
[2] = {
.name = "Usain Bolt",
.height = 195
},
},
.num_elements = 3
},
[1] = {
.elements = {
[0] = {
.name = "Muggsy Bogues",
.height = 160
},
[1] = {
.name = "Hakeem Olajuwon",
.height = 213
},
},
.num_elements = 2
},
[2] = {
.elements = {
[0] = {
.name = "Alex Honnold",
.height = 180
},
[1] = {
.name = "Hazel Findlay",
.height = 157
},
[2] = {
.name = "Daila Ojeda",
.height = 158
},
[3] = {
.name = "Albert Einstein",
.height = 172
},
},
.num_elements = 4
},
},
.obj_array_2dim.objects_array_array_len = 3,
};
char encoded[] = "{\"name\":\"Paavo Nurmi\",\"val\":123,"
"\"obj_array_2dim\":["
"[{\"name\":\"Sim\303\263n Bol\303\255var\",\"height\":168},"
"{\"name\":\"Pel\303\251\",\"height\":173},"
"{\"name\":\"Usain Bolt\",\"height\":195}],"
"[{\"name\":\"Muggsy Bogues\",\"height\":160},"
"{\"name\":\"Hakeem Olajuwon\",\"height\":213}],"
"[{\"name\":\"Alex Honnold\",\"height\":180},"
"{\"name\":\"Hazel Findlay\",\"height\":157},"
"{\"name\":\"Daila Ojeda\",\"height\":158},"
"{\"name\":\"Albert Einstein\",\"height\":172}]"
"]}";
char buffer[sizeof(encoded)];
int ret;
ret = json_obj_encode_buf(array_2dim_extra_descr, ARRAY_SIZE(array_2dim_extra_descr),
&obj_array_2dim_extra_ts, buffer, sizeof(buffer));
zassert_equal(ret, 0, "Encoding two-dimensional extra array returned error");
zassert_true(!strcmp(buffer, encoded),
"Encoded two-dimensional extra array is not consistent");
}
ZTEST(lib_json_test, test_json_2dim_arr_extra_named_obj_encoding)
{
struct obj_array_2dim_extra obj_array_2dim_extra_ts = {
.name = "Paavo Nurmi",
.val = 123,
.obj_array_2dim.objects_array_array = {
[0] = {
.elements = {
[0] = {
.name = "Sim\303\263n Bol\303\255var",
.height = 168
},
[1] = {
.name = "Pel\303\251",
.height = 173
},
[2] = {
.name = "Usain Bolt",
.height = 195
},
},
.num_elements = 3
},
[1] = {
.elements = {
[0] = {
.name = "Muggsy Bogues",
.height = 160
},
[1] = {
.name = "Hakeem Olajuwon",
.height = 213
},
},
.num_elements = 2
},
[2] = {
.elements = {
[0] = {
.name = "Alex Honnold",
.height = 180
},
[1] = {
.name = "Hazel Findlay",
.height = 157
},
[2] = {
.name = "Daila Ojeda",
.height = 158
},
[3] = {
.name = "Albert Einstein",
.height = 172
},
},
.num_elements = 4
},
},
.obj_array_2dim.objects_array_array_len = 3,
};
char encoded[] = "{\"name\":\"Paavo Nurmi\",\"val\":123,"
"\"data\":["
"[{\"name\":\"Sim\303\263n Bol\303\255var\",\"height\":168},"
"{\"name\":\"Pel\303\251\",\"height\":173},"
"{\"name\":\"Usain Bolt\",\"height\":195}],"
"[{\"name\":\"Muggsy Bogues\",\"height\":160},"
"{\"name\":\"Hakeem Olajuwon\",\"height\":213}],"
"[{\"name\":\"Alex Honnold\",\"height\":180},"
"{\"name\":\"Hazel Findlay\",\"height\":157},"
"{\"name\":\"Daila Ojeda\",\"height\":158},"
"{\"name\":\"Albert Einstein\",\"height\":172}]"
"]}";
char buffer[sizeof(encoded)];
int ret;
ret = json_obj_encode_buf(array_2dim_extra_named_descr,
ARRAY_SIZE(array_2dim_extra_named_descr),
&obj_array_2dim_extra_ts, buffer, sizeof(buffer));
zassert_equal(ret, 0, "Encoding two-dimensional extra named array returned error");
zassert_true(!strcmp(buffer, encoded),
"Encoded two-dimensional extra named array is not consistent");
}
ZTEST(lib_json_test, test_json_2dim_obj_arr_decoding)
{
struct obj_array_2dim oaa;
char encoded[] = "{\"objects_array_array\":["
"[{\"name\":\"Sim\303\263n Bol\303\255var\",\"height\":168},"
"{\"name\":\"Pel\303\251\",\"height\":173},"
"{\"name\":\"Usain Bolt\",\"height\":195}],"
"[{\"name\":\"Muggsy Bogues\",\"height\":160},"
"{\"name\":\"Hakeem Olajuwon\",\"height\":213}],"
"[{\"name\":\"Alex Honnold\",\"height\":180},"
"{\"name\":\"Hazel Findlay\",\"height\":157},"
"{\"name\":\"Daila Ojeda\",\"height\":158},"
"{\"name\":\"Albert Einstein\",\"height\":172}]"
"]}";
const struct obj_array_2dim expected = {
.objects_array_array = {
[0] = {
.elements = {
[0] = {
.name = "Sim\303\263n Bol\303\255var",
.height = 168
},
[1] = {
.name = "Pel\303\251",
.height = 173
},
[2] = {
.name = "Usain Bolt",
.height = 195
},
},
.num_elements = 3
},
[1] = {
.elements = {
[0] = {
.name = "Muggsy Bogues",
.height = 160
},
[1] = {
.name = "Hakeem Olajuwon",
.height = 213
},
},
.num_elements = 2
},
[2] = {
.elements = {
[0] = {
.name = "Alex Honnold",
.height = 180
},
[1] = {
.name = "Hazel Findlay",
.height = 157
},
[2] = {
.name = "Daila Ojeda",
.height = 158
},
[3] = {
.name = "Albert Einstein",
.height = 172
},
},
.num_elements = 4
},
},
.objects_array_array_len = 3,
};
int ret;
ret = json_obj_parse(encoded, sizeof(encoded),
array_2dim_descr,
ARRAY_SIZE(array_2dim_descr),
&oaa);
zassert_equal(ret, 1, "Array of arrays fields not decoded correctly");
zassert_equal(oaa.objects_array_array_len, 3,
"Number of subarrays not decoded correctly");
zassert_equal(oaa.objects_array_array[0].num_elements, 3,
"Number of object fields not decoded correctly");
zassert_equal(oaa.objects_array_array[1].num_elements, 2,
"Number of object fields not decoded correctly");
zassert_equal(oaa.objects_array_array[2].num_elements, 4,
"Number of object fields not decoded correctly");
for (int i = 0; i < expected.objects_array_array_len; i++) {
for (int j = 0; j < expected.objects_array_array[i].num_elements; j++) {
zassert_true(!strcmp(oaa.objects_array_array[i].elements[j].name,
expected.objects_array_array[i].elements[j].name),
"Element [%d][%d] name not decoded correctly", i, j);
zassert_equal(oaa.objects_array_array[i].elements[j].height,
expected.objects_array_array[i].elements[j].height,
"Element [%d][%d] height not decoded correctly", i, j);
}
}
}
struct encoding_test {
char *str;
int result;
};
static void parse_harness(struct encoding_test encoded[], size_t size)
{
struct test_struct ts;
int ret;
for (int i = 0; i < size; i++) {
ret = json_obj_parse(encoded[i].str, strlen(encoded[i].str),
test_descr, ARRAY_SIZE(test_descr), &ts);
zassert_equal(ret, encoded[i].result,
"Decoding '%s' result %d, expected %d",
encoded[i].str, ret, encoded[i].result);
}
}
ZTEST(lib_json_test, test_json_invalid_string)
{
struct encoding_test encoded[] = {
{ "{\"some_string\":\"\\u@@@@\"}", -EINVAL },
{ "{\"some_string\":\"\\uA@@@\"}", -EINVAL },
{ "{\"some_string\":\"\\uAB@@\"}", -EINVAL },
{ "{\"some_string\":\"\\uABC@\"}", -EINVAL },
{ "{\"some_string\":\"\\X\"}", -EINVAL }
};
parse_harness(encoded, ARRAY_SIZE(encoded));
}
ZTEST(lib_json_test, test_json_invalid_bool)
{
struct encoding_test encoded[] = {
{ "{\"some_bool\":truffle }", -EINVAL},
{ "{\"some_bool\":fallacy }", -EINVAL},
};
parse_harness(encoded, ARRAY_SIZE(encoded));
}
ZTEST(lib_json_test, test_json_invalid_null)
{
struct encoding_test encoded[] = {
/* Parser will recognize 'null', but refuse to decode it */
{ "{\"some_string\":null }", -EINVAL},
/* Null spelled wrong */
{ "{\"some_string\":nutella }", -EINVAL},
};
parse_harness(encoded, ARRAY_SIZE(encoded));
}
ZTEST(lib_json_test, test_json_invalid_number)
{
struct encoding_test encoded[] = {
{ "{\"some_int\":xxx }", -EINVAL},
};
parse_harness(encoded, ARRAY_SIZE(encoded));
}
ZTEST(lib_json_test, test_json_missing_quote)
{
struct test_struct ts;
char encoded[] = "{\"some_string";
int ret;
ret = json_obj_parse(encoded, sizeof(encoded) - 1, test_descr,
ARRAY_SIZE(test_descr), &ts);
zassert_equal(ret, -EINVAL, "Decoding has to fail");
}
ZTEST(lib_json_test, test_json_wrong_token)
{
struct test_struct ts;
char encoded[] = "{\"some_string\",}";
int ret;
ret = json_obj_parse(encoded, sizeof(encoded) - 1, test_descr,
ARRAY_SIZE(test_descr), &ts);
zassert_equal(ret, -EINVAL, "Decoding has to fail");
}
ZTEST(lib_json_test, test_json_item_wrong_type)
{
struct test_struct ts;
char encoded[] = "{\"some_string\":false}";
int ret;
ret = json_obj_parse(encoded, sizeof(encoded) - 1, test_descr,
ARRAY_SIZE(test_descr), &ts);
zassert_equal(ret, -EINVAL, "Decoding has to fail");
}
ZTEST(lib_json_test, test_json_key_not_in_descr)
{
struct test_struct ts;
char encoded[] = "{\"key_not_in_descr\":123456}";
int ret;
ret = json_obj_parse(encoded, sizeof(encoded) - 1, test_descr,
ARRAY_SIZE(test_descr), &ts);
zassert_equal(ret, 0, "No items should be decoded");
}
ZTEST(lib_json_test, test_json_escape)
{
char buf[42];
char string[] = "\"abc"
"\\1`23"
"\bf'oo"
"\fbar"
"\nbaz"
"\rquux"
"\tfred\"";
const char *expected = "\\\"abc"
"\\\\1`23"
"\\bf'oo"
"\\fbar"
"\\nbaz"
"\\rquux"
"\\tfred\\\"";
size_t len;
ssize_t ret;
strncpy(buf, string, sizeof(buf) - 1);
len = strlen(buf);
ret = json_escape(buf, &len, sizeof(buf));
zassert_equal(ret, 0, "Escape did not succeed");
zassert_equal(len, sizeof(buf) - 1,
"Escaped length not computed correctly");
zassert_true(!strcmp(buf, expected),
"Escaped value is not correct");
}
/* Edge case: only one character, which must be escaped. */
ZTEST(lib_json_test, test_json_escape_one)
{
char buf[3] = {'\t', '\0', '\0'};
const char *expected = "\\t";
size_t len = strlen(buf);
ssize_t ret;
ret = json_escape(buf, &len, sizeof(buf));
zassert_equal(ret, 0,
"Escaping one character did not succeed");
zassert_equal(len, sizeof(buf) - 1,
"Escaping one character length is not correct");
zassert_true(!strcmp(buf, expected),
"Escaped value is not correct");
}
ZTEST(lib_json_test, test_json_escape_empty)
{
char empty[] = "";
size_t len = sizeof(empty) - 1;
ssize_t ret;
ret = json_escape(empty, &len, sizeof(empty));
zassert_equal(ret, 0, "Escaping empty string not successful");
zassert_equal(len, 0, "Length of empty escaped string is not zero");
zassert_equal(empty[0], '\0', "Empty string does not remain empty");
}
ZTEST(lib_json_test, test_json_escape_no_op)
{
char nothing_to_escape[] = "hello,world:!";
const char *expected = "hello,world:!";
size_t len = sizeof(nothing_to_escape) - 1;
ssize_t ret;
ret = json_escape(nothing_to_escape, &len, sizeof(nothing_to_escape));
zassert_equal(ret, 0, "Escape no-op not handled correctly");
zassert_equal(len, sizeof(nothing_to_escape) - 1,
"Changed length of already escaped string");
zassert_true(!strcmp(nothing_to_escape, expected),
"Altered string with nothing to escape");
}
ZTEST(lib_json_test, test_json_escape_bounds_check)
{
char not_enough_memory[] = "\tfoo";
size_t len = sizeof(not_enough_memory) - 1;
ssize_t ret;
ret = json_escape(not_enough_memory, &len, sizeof(not_enough_memory));
zassert_equal(ret, -ENOMEM, "Bounds check failed");
}
ZTEST(lib_json_test, test_json_encode_bounds_check)
{
struct number {
uint32_t val;
} str = { 0 };
const struct json_obj_descr descr[] = {
JSON_OBJ_DESCR_PRIM(struct number, val, JSON_TOK_NUMBER),
};
/* Encodes to {"val":0}\0 for a total of 10 bytes */
uint8_t buf[10];
ssize_t ret = json_obj_encode_buf(descr, ARRAY_SIZE(descr),
&str, buf, 10);
zassert_equal(ret, 0, "Encoding failed despite large enough buffer");
zassert_equal(strlen(buf), 9, "Encoded string length mismatch");
ret = json_obj_encode_buf(descr, ARRAY_SIZE(descr),
&str, buf, 9);
zassert_equal(ret, -ENOMEM, "Bounds check failed");
}
ZTEST(lib_json_test, test_large_descriptor)
{
struct large_struct {
int int0;
int int1;
int int2;
int int3;
int int4;
int int5;
int int6;
int int7;
int int8;
int int9;
int int10;
int int11;
int int12;
int int13;
int int14;
int int15;
int int16;
int int17;
int int18;
int int19;
int int20;
int int21;
int int22;
int int23;
int int24;
int int25;
int int26;
int int27;
int int28;
int int29;
int int30;
int int31;
int int32;
int int33;
int int34;
int int35;
int int36;
int int37;
int int38;
int int39;
};
static const struct json_obj_descr large_descr[] = {
JSON_OBJ_DESCR_PRIM(struct large_struct, int0, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int1, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int2, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int3, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int4, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int5, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int6, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int7, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int8, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int9, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int10, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int11, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int12, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int13, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int14, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int15, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int16, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int17, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int18, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int19, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int20, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int21, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int22, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int23, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int24, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int25, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int26, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int27, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int28, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int29, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int30, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int31, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int32, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int33, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int34, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int35, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int36, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int37, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int38, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct large_struct, int39, JSON_TOK_NUMBER),
};
char encoded[] = "{"
"\"int1\": 1,"
"\"int21\": 21,"
"\"int31\": 31,"
"\"int39\": 39"
"}";
struct large_struct ls;
int64_t ret = json_obj_parse(encoded, sizeof(encoded) - 1, large_descr,
ARRAY_SIZE(large_descr), &ls);
zassert_false(ret < 0, "json_obj_parse returned error %d", ret);
zassert_false(ret & ((int64_t)1 << 2), "Field int2 erroneously decoded");
zassert_false(ret & ((int64_t)1 << 35), "Field int35 erroneously decoded");
zassert_true(ret & ((int64_t)1 << 1), "Field int1 not decoded");
zassert_true(ret & ((int64_t)1 << 21), "Field int21 not decoded");
zassert_true(ret & ((int64_t)1 << 31), "Field int31 not decoded");
zassert_true(ret & ((int64_t)1 << 39), "Field int39 not decoded");
}
ZTEST_SUITE(lib_json_test, NULL, NULL, NULL, NULL, NULL);