diff options
Diffstat (limited to 'src/spdk/test/unit/lib/json')
-rw-r--r-- | src/spdk/test/unit/lib/json/Makefile | 44 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_parse.c/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_parse.c/Makefile | 38 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_parse.c/json_parse_ut.c | 931 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_util.c/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_util.c/Makefile | 38 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_util.c/json_util_ut.c | 954 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_write.c/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_write.c/Makefile | 38 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/json/json_write.c/json_write_ut.c | 736 |
10 files changed, 2782 insertions, 0 deletions
diff --git a/src/spdk/test/unit/lib/json/Makefile b/src/spdk/test/unit/lib/json/Makefile new file mode 100644 index 000000000..db38f27dc --- /dev/null +++ b/src/spdk/test/unit/lib/json/Makefile @@ -0,0 +1,44 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +DIRS-y = json_parse.c json_util.c json_write.c + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) +clean: $(DIRS-y) + +include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk diff --git a/src/spdk/test/unit/lib/json/json_parse.c/.gitignore b/src/spdk/test/unit/lib/json/json_parse.c/.gitignore new file mode 100644 index 000000000..2b4445fd8 --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_parse.c/.gitignore @@ -0,0 +1 @@ +json_parse_ut diff --git a/src/spdk/test/unit/lib/json/json_parse.c/Makefile b/src/spdk/test/unit/lib/json/json_parse.c/Makefile new file mode 100644 index 000000000..3d4100240 --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_parse.c/Makefile @@ -0,0 +1,38 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) + +TEST_FILE = json_parse_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/json/json_parse.c/json_parse_ut.c b/src/spdk/test/unit/lib/json/json_parse.c/json_parse_ut.c new file mode 100644 index 000000000..7f704214b --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_parse.c/json_parse_ut.c @@ -0,0 +1,931 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "spdk_cunit.h" + +#include "json/json_parse.c" + +static uint8_t g_buf[1000]; +static void *g_end; +static struct spdk_json_val g_vals[100]; +static int g_cur_val; + +/* Fill buf with raw data */ +#define BUF_SETUP(in) \ + memset(g_buf, 0, sizeof(g_buf)); \ + if (sizeof(in) > 1) { \ + memcpy(g_buf, in, sizeof(in) - 1); \ + } \ + g_end = NULL + +/* + * Do two checks - first pass NULL for values to ensure the count is correct, + * then pass g_vals to get the actual values. + */ +#define PARSE_PASS_FLAGS(in, num_vals, trailing, flags) \ + BUF_SETUP(in); \ + CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, NULL, 0, &g_end, flags) == num_vals); \ + memset(g_vals, 0, sizeof(g_vals)); \ + CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, g_vals, sizeof(g_vals), &g_end, flags | SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE) == num_vals); \ + CU_ASSERT(g_end == g_buf + sizeof(in) - sizeof(trailing)); \ + CU_ASSERT(memcmp(g_end, trailing, sizeof(trailing) - 1) == 0); \ + g_cur_val = 0 + +#define PARSE_PASS(in, num_vals, trailing) \ + PARSE_PASS_FLAGS(in, num_vals, trailing, 0) + +#define PARSE_FAIL_FLAGS(in, retval, flags) \ + BUF_SETUP(in); \ + CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, NULL, 0, &g_end, flags) == retval) + +#define PARSE_FAIL(in, retval) \ + PARSE_FAIL_FLAGS(in, retval, 0) + +#define VAL_STRING_MATCH(str, var_type) \ + CU_ASSERT(g_vals[g_cur_val].type == var_type); \ + CU_ASSERT(g_vals[g_cur_val].len == sizeof(str) - 1); \ + if (g_vals[g_cur_val].len == sizeof(str) - 1 && sizeof(str) > 1) { \ + CU_ASSERT(memcmp(g_vals[g_cur_val].start, str, g_vals[g_cur_val].len) == 0); \ + } \ + g_cur_val++ + +#define VAL_STRING(str) VAL_STRING_MATCH(str, SPDK_JSON_VAL_STRING) +#define VAL_NAME(str) VAL_STRING_MATCH(str, SPDK_JSON_VAL_NAME) +#define VAL_NUMBER(num) VAL_STRING_MATCH(num, SPDK_JSON_VAL_NUMBER) + +#define VAL_LITERAL(str, val_type) \ + CU_ASSERT(g_vals[g_cur_val].type == val_type); \ + CU_ASSERT(g_vals[g_cur_val].len == strlen(str)); \ + if (g_vals[g_cur_val].len == strlen(str)) { \ + CU_ASSERT(memcmp(g_vals[g_cur_val].start, str, g_vals[g_cur_val].len) == 0); \ + } \ + g_cur_val++ + +#define VAL_TRUE() VAL_LITERAL("true", SPDK_JSON_VAL_TRUE) +#define VAL_FALSE() VAL_LITERAL("false", SPDK_JSON_VAL_FALSE) +#define VAL_NULL() VAL_LITERAL("null", SPDK_JSON_VAL_NULL) + +#define VAL_ARRAY_BEGIN(count) \ + CU_ASSERT(g_vals[g_cur_val].type == SPDK_JSON_VAL_ARRAY_BEGIN); \ + CU_ASSERT(g_vals[g_cur_val].len == count); \ + g_cur_val++ + +#define VAL_ARRAY_END() \ + CU_ASSERT(g_vals[g_cur_val].type == SPDK_JSON_VAL_ARRAY_END); \ + g_cur_val++ + +#define VAL_OBJECT_BEGIN(count) \ + CU_ASSERT(g_vals[g_cur_val].type == SPDK_JSON_VAL_OBJECT_BEGIN); \ + CU_ASSERT(g_vals[g_cur_val].len == count); \ + g_cur_val++ + +#define VAL_OBJECT_END() \ + CU_ASSERT(g_vals[g_cur_val].type == SPDK_JSON_VAL_OBJECT_END); \ + g_cur_val++ + +/* Simplified macros for string-only testing */ +#define STR_PASS(in, out) \ + PARSE_PASS("\"" in "\"", 1, ""); \ + VAL_STRING(out) + +#define STR_FAIL(in, retval) \ + PARSE_FAIL("\"" in "\"", retval) + +/* Simplified macros for number-only testing (no whitespace allowed) */ +#define NUM_PASS(in) \ + PARSE_PASS(in, 1, ""); \ + VAL_NUMBER(in) + +#define NUM_FAIL(in, retval) \ + PARSE_FAIL(in, retval) + +static void +test_parse_literal(void) +{ + PARSE_PASS("true", 1, ""); + VAL_TRUE(); + + PARSE_PASS(" true ", 1, ""); + VAL_TRUE(); + + PARSE_PASS("false", 1, ""); + VAL_FALSE(); + + PARSE_PASS("null", 1, ""); + VAL_NULL(); + + PARSE_PASS("trueaaa", 1, "aaa"); + VAL_TRUE(); + + PARSE_PASS("truefalse", 1, "false"); + VAL_TRUE(); + + PARSE_PASS("true false", 1, "false"); + VAL_TRUE(); + + PARSE_PASS("true,false", 1, ",false"); + VAL_TRUE(); + + PARSE_PASS("true,", 1, ","); + VAL_TRUE(); + + PARSE_FAIL("True", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("abcdef", SPDK_JSON_PARSE_INVALID); + + PARSE_FAIL("t", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("tru", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("f", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("fals", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("n", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("nul", SPDK_JSON_PARSE_INCOMPLETE); + + PARSE_FAIL("taaaaa", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("faaaaa", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("naaaaa", SPDK_JSON_PARSE_INVALID); +} + +static void +test_parse_string_simple(void) +{ + PARSE_PASS("\"\"", 1, ""); + VAL_STRING(""); + + PARSE_PASS("\"hello world\"", 1, ""); + VAL_STRING("hello world"); + + PARSE_PASS(" \"hello world\" ", 1, ""); + VAL_STRING("hello world"); + + /* Unterminated string */ + PARSE_FAIL("\"hello world", SPDK_JSON_PARSE_INCOMPLETE); + + /* Trailing comma */ + PARSE_PASS("\"hello world\",", 1, ","); + VAL_STRING("hello world"); +} + +static void +test_parse_string_control_chars(void) +{ + /* U+0000 through U+001F must be escaped */ + STR_FAIL("\x00", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x01", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x02", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x03", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x04", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x05", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x06", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x07", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x08", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x09", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x0A", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x0B", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x0C", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x0D", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x0E", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x0F", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x10", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x11", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x12", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x13", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x14", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x15", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x16", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x17", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x18", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x19", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x1A", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x1B", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x1C", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x1D", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x1E", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\x1F", SPDK_JSON_PARSE_INVALID); + STR_PASS(" ", " "); /* \x20 (first valid unescaped char) */ + + /* Test control chars in the middle of a string */ + STR_FAIL("abc\ndef", SPDK_JSON_PARSE_INVALID); + STR_FAIL("abc\tdef", SPDK_JSON_PARSE_INVALID); +} + +static void +test_parse_string_utf8(void) +{ + /* Valid one-, two-, three-, and four-byte sequences */ + STR_PASS("\x41", "A"); + STR_PASS("\xC3\xB6", "\xC3\xB6"); + STR_PASS("\xE2\x88\x9A", "\xE2\x88\x9A"); + STR_PASS("\xF0\xA0\x9C\x8E", "\xF0\xA0\x9C\x8E"); + + /* Examples from RFC 3629 */ + STR_PASS("\x41\xE2\x89\xA2\xCE\x91\x2E", "\x41\xE2\x89\xA2\xCE\x91\x2E"); + STR_PASS("\xED\x95\x9C\xEA\xB5\xAD\xEC\x96\xB4", "\xED\x95\x9C\xEA\xB5\xAD\xEC\x96\xB4"); + STR_PASS("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E", "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"); + STR_PASS("\xEF\xBB\xBF\xF0\xA3\x8E\xB4", "\xEF\xBB\xBF\xF0\xA3\x8E\xB4"); + + /* Edge cases */ + STR_PASS("\x7F", "\x7F"); + STR_FAIL("\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xC1", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xC2", SPDK_JSON_PARSE_INVALID); + STR_PASS("\xC2\x80", "\xC2\x80"); + STR_PASS("\xC2\xBF", "\xC2\xBF"); + STR_PASS("\xDF\x80", "\xDF\x80"); + STR_PASS("\xDF\xBF", "\xDF\xBF"); + STR_FAIL("\xDF", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE0\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE0\x1F", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE0\x1F\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE0", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE0\xA0", SPDK_JSON_PARSE_INVALID); + STR_PASS("\xE0\xA0\x80", "\xE0\xA0\x80"); + STR_PASS("\xE0\xA0\xBF", "\xE0\xA0\xBF"); + STR_FAIL("\xE0\xA0\xC0", SPDK_JSON_PARSE_INVALID); + STR_PASS("\xE0\xBF\x80", "\xE0\xBF\x80"); + STR_PASS("\xE0\xBF\xBF", "\xE0\xBF\xBF"); + STR_FAIL("\xE0\xC0\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE1", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE1\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE1\x7F\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE1\x80\x7F", SPDK_JSON_PARSE_INVALID); + STR_PASS("\xE1\x80\x80", "\xE1\x80\x80"); + STR_PASS("\xE1\x80\xBF", "\xE1\x80\xBF"); + STR_PASS("\xE1\xBF\x80", "\xE1\xBF\x80"); + STR_PASS("\xE1\xBF\xBF", "\xE1\xBF\xBF"); + STR_FAIL("\xE1\xC0\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xE1\x80\xC0", SPDK_JSON_PARSE_INVALID); + STR_PASS("\xEF\x80\x80", "\xEF\x80\x80"); + STR_PASS("\xEF\xBF\xBF", "\xEF\xBF\xBF"); + STR_FAIL("\xF0", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF0\x90", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF0\x90\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF0\x80\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF0\x8F\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_PASS("\xF0\x90\x80\x80", "\xF0\x90\x80\x80"); + STR_PASS("\xF0\x90\x80\xBF", "\xF0\x90\x80\xBF"); + STR_PASS("\xF0\x90\xBF\x80", "\xF0\x90\xBF\x80"); + STR_PASS("\xF0\xBF\x80\x80", "\xF0\xBF\x80\x80"); + STR_FAIL("\xF0\xC0\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF1", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF1\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF1\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF1\x80\x80\x7F", SPDK_JSON_PARSE_INVALID); + STR_PASS("\xF1\x80\x80\x80", "\xF1\x80\x80\x80"); + STR_PASS("\xF1\x80\x80\xBF", "\xF1\x80\x80\xBF"); + STR_PASS("\xF1\x80\xBF\x80", "\xF1\x80\xBF\x80"); + STR_PASS("\xF1\xBF\x80\x80", "\xF1\xBF\x80\x80"); + STR_PASS("\xF3\x80\x80\x80", "\xF3\x80\x80\x80"); + STR_FAIL("\xF3\xC0\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF3\x80\xC0\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF3\x80\x80\xC0", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF4", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF4\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF4\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_PASS("\xF4\x80\x80\x80", "\xF4\x80\x80\x80"); + STR_PASS("\xF4\x8F\x80\x80", "\xF4\x8F\x80\x80"); + STR_PASS("\xF4\x8F\xBF\xBF", "\xF4\x8F\xBF\xBF"); + STR_FAIL("\xF4\x90\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF5", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF5\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF5\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF5\x80\x80\x80", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\xF5\x80\x80\x80\x80", SPDK_JSON_PARSE_INVALID); + + /* Overlong encodings */ + STR_FAIL("\xC0\x80", SPDK_JSON_PARSE_INVALID); + + /* Surrogate pairs */ + STR_FAIL("\xED\xA0\x80", SPDK_JSON_PARSE_INVALID); /* U+D800 First high surrogate */ + STR_FAIL("\xED\xAF\xBF", SPDK_JSON_PARSE_INVALID); /* U+DBFF Last high surrogate */ + STR_FAIL("\xED\xB0\x80", SPDK_JSON_PARSE_INVALID); /* U+DC00 First low surrogate */ + STR_FAIL("\xED\xBF\xBF", SPDK_JSON_PARSE_INVALID); /* U+DFFF Last low surrogate */ + STR_FAIL("\xED\xA1\x8C\xED\xBE\xB4", + SPDK_JSON_PARSE_INVALID); /* U+233B4 (invalid surrogate pair encoding) */ +} + +static void +test_parse_string_escapes_twochar(void) +{ + STR_PASS("\\\"", "\""); + STR_PASS("\\\\", "\\"); + STR_PASS("\\/", "/"); + STR_PASS("\\b", "\b"); + STR_PASS("\\f", "\f"); + STR_PASS("\\n", "\n"); + STR_PASS("\\r", "\r"); + STR_PASS("\\t", "\t"); + + STR_PASS("abc\\tdef", "abc\tdef"); + STR_PASS("abc\\\"def", "abc\"def"); + + /* Backslash at end of string (will be treated as escaped quote) */ + STR_FAIL("\\", SPDK_JSON_PARSE_INCOMPLETE); + STR_FAIL("abc\\", SPDK_JSON_PARSE_INCOMPLETE); + + /* Invalid C-like escapes */ + STR_FAIL("\\a", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\v", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\'", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\?", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\0", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\x00", SPDK_JSON_PARSE_INVALID); + + /* Other invalid escapes */ + STR_FAIL("\\B", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\z", SPDK_JSON_PARSE_INVALID); +} + +static void +test_parse_string_escapes_unicode(void) +{ + STR_PASS("\\u0000", "\0"); + STR_PASS("\\u0001", "\1"); + STR_PASS("\\u0041", "A"); + STR_PASS("\\uAAAA", "\xEA\xAA\xAA"); + STR_PASS("\\uaaaa", "\xEA\xAA\xAA"); + STR_PASS("\\uAaAa", "\xEA\xAA\xAA"); + + STR_FAIL("\\u", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\u0", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\u00", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\u000", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\u000g", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\U", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\U0000", SPDK_JSON_PARSE_INVALID); + + PARSE_FAIL("\"\\u", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("\"\\u0", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("\"\\u00", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("\"\\u000", SPDK_JSON_PARSE_INCOMPLETE); + + /* Surrogate pair */ + STR_PASS("\\uD834\\uDD1E", "\xF0\x9D\x84\x9E"); + + /* Low surrogate without high */ + STR_FAIL("\\uDC00", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\uDC00\\uDC00", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\uDC00abcdef", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\uDEAD", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("\"\\uD834", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("\"\\uD834\\", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("\"\\uD834\\u", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("\"\\uD834\\uD", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("\"\\uD834\\uDD1", SPDK_JSON_PARSE_INCOMPLETE); + + /* High surrogate without low */ + STR_FAIL("\\uD800", SPDK_JSON_PARSE_INVALID); + STR_FAIL("\\uD800abcdef", SPDK_JSON_PARSE_INVALID); + + /* High surrogate followed by high surrogate */ + STR_FAIL("\\uD800\\uD800", SPDK_JSON_PARSE_INVALID); +} + +static void +test_parse_number(void) +{ + NUM_PASS("0"); + NUM_PASS("1"); + NUM_PASS("100"); + NUM_PASS("-1"); + NUM_PASS("-0"); + NUM_PASS("3.0"); + NUM_PASS("3.00"); + NUM_PASS("3.001"); + NUM_PASS("3.14159"); + NUM_PASS("3.141592653589793238462643383279"); + NUM_PASS("1e400"); + NUM_PASS("1E400"); + NUM_PASS("0e10"); + NUM_PASS("0e0"); + NUM_PASS("-0e0"); + NUM_PASS("-0e+0"); + NUM_PASS("-0e-0"); + NUM_PASS("1e+400"); + NUM_PASS("1e-400"); + NUM_PASS("6.022e23"); + NUM_PASS("-1.234e+56"); + NUM_PASS("1.23e+56"); + NUM_PASS("-1.23e-56"); + NUM_PASS("1.23e-56"); + NUM_PASS("1e04"); + + /* Trailing garbage */ + PARSE_PASS("0A", 1, "A"); + VAL_NUMBER("0"); + + PARSE_PASS("0,", 1, ","); + VAL_NUMBER("0"); + + PARSE_PASS("0true", 1, "true"); + VAL_NUMBER("0"); + + PARSE_PASS("00", 1, "0"); + VAL_NUMBER("0"); + PARSE_FAIL("[00", SPDK_JSON_PARSE_INVALID); + + PARSE_PASS("007", 1, "07"); + VAL_NUMBER("0"); + PARSE_FAIL("[007]", SPDK_JSON_PARSE_INVALID); + + PARSE_PASS("345.678.1", 1, ".1"); + VAL_NUMBER("345.678"); + PARSE_FAIL("[345.678.1]", SPDK_JSON_PARSE_INVALID); + + PARSE_PASS("3.2e-4+5", 1, "+5"); + VAL_NUMBER("3.2e-4"); + PARSE_FAIL("[3.2e-4+5]", SPDK_JSON_PARSE_INVALID); + + PARSE_PASS("3.4.5", 1, ".5"); + VAL_NUMBER("3.4"); + PARSE_FAIL("[3.4.5]", SPDK_JSON_PARSE_INVALID); + + NUM_FAIL("345.", SPDK_JSON_PARSE_INCOMPLETE); + NUM_FAIL("+1", SPDK_JSON_PARSE_INVALID); + NUM_FAIL("--1", SPDK_JSON_PARSE_INVALID); + NUM_FAIL("3.", SPDK_JSON_PARSE_INCOMPLETE); + NUM_FAIL("3.+4", SPDK_JSON_PARSE_INVALID); + NUM_FAIL("3.2e+-4", SPDK_JSON_PARSE_INVALID); + NUM_FAIL("3.2e-+4", SPDK_JSON_PARSE_INVALID); + NUM_FAIL("3e+", SPDK_JSON_PARSE_INCOMPLETE); + NUM_FAIL("3e-", SPDK_JSON_PARSE_INCOMPLETE); + NUM_FAIL("3.e4", SPDK_JSON_PARSE_INVALID); + NUM_FAIL("3.2eX", SPDK_JSON_PARSE_INVALID); + NUM_FAIL("-", SPDK_JSON_PARSE_INCOMPLETE); + NUM_FAIL("NaN", SPDK_JSON_PARSE_INVALID); + NUM_FAIL(".123", SPDK_JSON_PARSE_INVALID); +} + +static void +test_parse_array(void) +{ + char buffer[SPDK_JSON_MAX_NESTING_DEPTH + 2] = {0}; + + PARSE_PASS("[]", 2, ""); + VAL_ARRAY_BEGIN(0); + VAL_ARRAY_END(); + + PARSE_PASS("[true]", 3, ""); + VAL_ARRAY_BEGIN(1); + VAL_TRUE(); + VAL_ARRAY_END(); + + PARSE_PASS("[true, false]", 4, ""); + VAL_ARRAY_BEGIN(2); + VAL_TRUE(); + VAL_FALSE(); + VAL_ARRAY_END(); + + PARSE_PASS("[\"hello\"]", 3, ""); + VAL_ARRAY_BEGIN(1); + VAL_STRING("hello"); + VAL_ARRAY_END(); + + PARSE_PASS("[[]]", 4, ""); + VAL_ARRAY_BEGIN(2); + VAL_ARRAY_BEGIN(0); + VAL_ARRAY_END(); + VAL_ARRAY_END(); + + PARSE_PASS("[\"hello\", \"world\"]", 4, ""); + VAL_ARRAY_BEGIN(2); + VAL_STRING("hello"); + VAL_STRING("world"); + VAL_ARRAY_END(); + + PARSE_PASS("[],", 2, ","); + VAL_ARRAY_BEGIN(0); + VAL_ARRAY_END(); + + PARSE_FAIL("]", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("[", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("[true", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("[\"hello", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("[\"hello\"", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("[true,]", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("[,]", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("[,true]", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("[true}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("[true,,true]", SPDK_JSON_PARSE_INVALID); + + /* Nested arrays exactly up to the allowed nesting depth */ + memset(buffer, '[', SPDK_JSON_MAX_NESTING_DEPTH); + buffer[SPDK_JSON_MAX_NESTING_DEPTH] = ' '; + PARSE_FAIL(buffer, SPDK_JSON_PARSE_INCOMPLETE); + + /* Nested arrays exceeding the maximum allowed nesting depth for this implementation */ + buffer[SPDK_JSON_MAX_NESTING_DEPTH] = '['; + PARSE_FAIL(buffer, SPDK_JSON_PARSE_MAX_DEPTH_EXCEEDED); +} + +static void +test_parse_object(void) +{ + PARSE_PASS("{}", 2, ""); + VAL_OBJECT_BEGIN(0); + VAL_OBJECT_END(); + + PARSE_PASS("{\"a\": true}", 4, ""); + VAL_OBJECT_BEGIN(2); + VAL_NAME("a"); + VAL_TRUE(); + VAL_OBJECT_END(); + + PARSE_PASS("{\"abc\": \"def\"}", 4, ""); + VAL_OBJECT_BEGIN(2); + VAL_NAME("abc"); + VAL_STRING("def"); + VAL_OBJECT_END(); + + PARSE_PASS("{\"a\": true, \"b\": false}", 6, ""); + VAL_OBJECT_BEGIN(4); + VAL_NAME("a"); + VAL_TRUE(); + VAL_NAME("b"); + VAL_FALSE(); + VAL_OBJECT_END(); + + PARSE_PASS("{\"a\": { \"b\": true } }", 7, ""); + VAL_OBJECT_BEGIN(5); + VAL_NAME("a"); + VAL_OBJECT_BEGIN(2); + VAL_NAME("b"); + VAL_TRUE(); + VAL_OBJECT_END(); + VAL_OBJECT_END(); + + PARSE_PASS("{\"{test\": 0}", 4, ""); + VAL_OBJECT_BEGIN(2); + VAL_NAME("{test"); + VAL_NUMBER("0"); + VAL_OBJECT_END(); + + PARSE_PASS("{\"test}\": 1}", 4, ""); + VAL_OBJECT_BEGIN(2); + VAL_NAME("test}"); + VAL_NUMBER("1"); + VAL_OBJECT_END(); + + PARSE_PASS("{\"\\\"\": 2}", 4, ""); + VAL_OBJECT_BEGIN(2); + VAL_NAME("\""); + VAL_NUMBER("2"); + VAL_OBJECT_END(); + + PARSE_PASS("{\"a\":true},", 4, ","); + VAL_OBJECT_BEGIN(2); + VAL_NAME("a"); + VAL_TRUE(); + VAL_OBJECT_END(); + + /* Object end without object begin (trailing garbage) */ + PARSE_PASS("true}", 1, "}"); + VAL_TRUE(); + + PARSE_PASS("0}", 1, "}"); + VAL_NUMBER("0"); + + PARSE_PASS("\"a\"}", 1, "}"); + VAL_STRING("a"); + + PARSE_FAIL("}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("{\"a", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("{\"a\"", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("{\"a\":", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("{\"a\":true", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("{\"a\":true,", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("{\"a\":true]", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{\"a\":true,}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{\"a\":true,\"}", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("{\"a\":true,\"b}", SPDK_JSON_PARSE_INCOMPLETE); + PARSE_FAIL("{\"a\":true,\"b\"}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{\"a\":true,\"b\":}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{\"a\":true,\"b\",}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{\"a\",}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{,\"a\": true}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{a:true}", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{'a':true}", SPDK_JSON_PARSE_INVALID); +} + +static void +test_parse_nesting(void) +{ + PARSE_PASS("[[[[[[[[]]]]]]]]", 16, ""); + + PARSE_PASS("{\"a\": [0, 1, 2]}", 8, ""); + VAL_OBJECT_BEGIN(6); + VAL_NAME("a"); + VAL_ARRAY_BEGIN(3); + VAL_NUMBER("0"); + VAL_NUMBER("1"); + VAL_NUMBER("2"); + VAL_ARRAY_END(); + VAL_OBJECT_END(); + + PARSE_PASS("{\"a\": [0, 1, 2], \"b\": 3 }", 10, ""); + VAL_OBJECT_BEGIN(8); + VAL_NAME("a"); + VAL_ARRAY_BEGIN(3); + VAL_NUMBER("0"); + VAL_NUMBER("1"); + VAL_NUMBER("2"); + VAL_ARRAY_END(); + VAL_NAME("b"); + VAL_NUMBER("3"); + VAL_OBJECT_END(); + + PARSE_PASS("[0, 1, {\"a\": 3}, 4, 5]", 10, ""); + VAL_ARRAY_BEGIN(8); + VAL_NUMBER("0"); + VAL_NUMBER("1"); + VAL_OBJECT_BEGIN(2); + VAL_NAME("a"); + VAL_NUMBER("3"); + VAL_OBJECT_END(); + VAL_NUMBER("4"); + VAL_NUMBER("5"); + VAL_ARRAY_END(); + + PARSE_PASS("\t[ { \"a\": {\"b\": [ {\"c\": 1}, 2 ],\n\"d\": 3}, \"e\" : 4}, 5 ] ", 20, ""); + VAL_ARRAY_BEGIN(18); + VAL_OBJECT_BEGIN(15); + VAL_NAME("a"); + VAL_OBJECT_BEGIN(10); + VAL_NAME("b"); + VAL_ARRAY_BEGIN(5); + VAL_OBJECT_BEGIN(2); + VAL_NAME("c"); + VAL_NUMBER("1"); + VAL_OBJECT_END(); + VAL_NUMBER("2"); + VAL_ARRAY_END(); + VAL_NAME("d"); + VAL_NUMBER("3"); + VAL_OBJECT_END(); + VAL_NAME("e"); + VAL_NUMBER("4"); + VAL_OBJECT_END(); + VAL_NUMBER("5"); + VAL_ARRAY_END(); + + /* Examples from RFC 7159 */ + PARSE_PASS( + "{\n" + " \"Image\": {\n" + " \"Width\": 800,\n" + " \"Height\": 600,\n" + " \"Title\": \"View from 15th Floor\",\n" + " \"Thumbnail\": {\n" + " \"Url\": \"http://www.example.com/image/481989943\",\n" + " \"Height\": 125,\n" + " \"Width\": 100\n" + " },\n" + " \"Animated\" : false,\n" + " \"IDs\": [116, 943, 234, 38793]\n" + " }\n" + "}\n", + 29, ""); + + VAL_OBJECT_BEGIN(27); + VAL_NAME("Image"); + VAL_OBJECT_BEGIN(24); + VAL_NAME("Width"); + VAL_NUMBER("800"); + VAL_NAME("Height"); + VAL_NUMBER("600"); + VAL_NAME("Title"); + VAL_STRING("View from 15th Floor"); + VAL_NAME("Thumbnail"); + VAL_OBJECT_BEGIN(6); + VAL_NAME("Url"); + VAL_STRING("http://www.example.com/image/481989943"); + VAL_NAME("Height"); + VAL_NUMBER("125"); + VAL_NAME("Width"); + VAL_NUMBER("100"); + VAL_OBJECT_END(); + VAL_NAME("Animated"); + VAL_FALSE(); + VAL_NAME("IDs"); + VAL_ARRAY_BEGIN(4); + VAL_NUMBER("116"); + VAL_NUMBER("943"); + VAL_NUMBER("234"); + VAL_NUMBER("38793"); + VAL_ARRAY_END(); + VAL_OBJECT_END(); + VAL_OBJECT_END(); + + PARSE_PASS( + "[\n" + " {\n" + " \"precision\": \"zip\",\n" + " \"Latitude\": 37.7668,\n" + " \"Longitude\": -122.3959,\n" + " \"Address\": \"\",\n" + " \"City\": \"SAN FRANCISCO\",\n" + " \"State\": \"CA\",\n" + " \"Zip\": \"94107\",\n" + " \"Country\": \"US\"\n" + " },\n" + " {\n" + " \"precision\": \"zip\",\n" + " \"Latitude\": 37.371991,\n" + " \"Longitude\": -122.026020,\n" + " \"Address\": \"\",\n" + " \"City\": \"SUNNYVALE\",\n" + " \"State\": \"CA\",\n" + " \"Zip\": \"94085\",\n" + " \"Country\": \"US\"\n" + " }\n" + "]", + 38, ""); + + VAL_ARRAY_BEGIN(36); + VAL_OBJECT_BEGIN(16); + VAL_NAME("precision"); + VAL_STRING("zip"); + VAL_NAME("Latitude"); + VAL_NUMBER("37.7668"); + VAL_NAME("Longitude"); + VAL_NUMBER("-122.3959"); + VAL_NAME("Address"); + VAL_STRING(""); + VAL_NAME("City"); + VAL_STRING("SAN FRANCISCO"); + VAL_NAME("State"); + VAL_STRING("CA"); + VAL_NAME("Zip"); + VAL_STRING("94107"); + VAL_NAME("Country"); + VAL_STRING("US"); + VAL_OBJECT_END(); + VAL_OBJECT_BEGIN(16); + VAL_NAME("precision"); + VAL_STRING("zip"); + VAL_NAME("Latitude"); + VAL_NUMBER("37.371991"); + VAL_NAME("Longitude"); + VAL_NUMBER("-122.026020"); + VAL_NAME("Address"); + VAL_STRING(""); + VAL_NAME("City"); + VAL_STRING("SUNNYVALE"); + VAL_NAME("State"); + VAL_STRING("CA"); + VAL_NAME("Zip"); + VAL_STRING("94085"); + VAL_NAME("Country"); + VAL_STRING("US"); + VAL_OBJECT_END(); + VAL_ARRAY_END(); + + /* Trailing garbage */ + PARSE_PASS("{\"a\": [0, 1, 2]}]", 8, "]"); + VAL_OBJECT_BEGIN(6); + VAL_NAME("a"); + VAL_ARRAY_BEGIN(3); + VAL_NUMBER("0"); + VAL_NUMBER("1"); + VAL_NUMBER("2"); + VAL_ARRAY_END(); + VAL_OBJECT_END(); + + PARSE_PASS("{\"a\": [0, 1, 2]}}", 8, "}"); + PARSE_PASS("{\"a\": [0, 1, 2]}]", 8, "]"); + VAL_OBJECT_BEGIN(6); + VAL_NAME("a"); + VAL_ARRAY_BEGIN(3); + VAL_NUMBER("0"); + VAL_NUMBER("1"); + VAL_NUMBER("2"); + VAL_ARRAY_END(); + VAL_OBJECT_END(); + + PARSE_FAIL("{\"a\": [0, 1, 2}]", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("{\"a\": [0, 1, 2]", SPDK_JSON_PARSE_INCOMPLETE); +} + + +static void +test_parse_comment(void) +{ + /* Comments are not allowed by the JSON RFC */ + PARSE_PASS("[0]", 3, ""); + PARSE_FAIL("/* test */[0]", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("[/* test */0]", SPDK_JSON_PARSE_INVALID); + PARSE_FAIL("[0/* test */]", SPDK_JSON_PARSE_INVALID); + + /* + * This is allowed since the parser stops once it reads a complete JSON object. + * The next parse call would fail (see tests above) when parsing the comment. + */ + PARSE_PASS("[0]/* test */", 3, "/* test */"); + + /* + * Test with non-standard comments enabled. + */ + PARSE_PASS_FLAGS("/* test */[0]", 3, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + VAL_ARRAY_BEGIN(1); + VAL_NUMBER("0"); + VAL_ARRAY_END(); + + PARSE_PASS_FLAGS("[/* test */0]", 3, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + VAL_ARRAY_BEGIN(1); + VAL_NUMBER("0"); + VAL_ARRAY_END(); + + PARSE_PASS_FLAGS("[0/* test */]", 3, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + VAL_ARRAY_BEGIN(1); + VAL_NUMBER("0"); + VAL_ARRAY_END(); + + PARSE_FAIL_FLAGS("/* test */", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + PARSE_FAIL_FLAGS("[/* test */", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + PARSE_FAIL_FLAGS("[0/* test */", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + + /* + * Single-line comments + */ + PARSE_PASS_FLAGS("// test\n0", 1, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + VAL_NUMBER("0"); + + PARSE_PASS_FLAGS("// test\r\n0", 1, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + VAL_NUMBER("0"); + + PARSE_PASS_FLAGS("// [0] test\n0", 1, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + VAL_NUMBER("0"); + + PARSE_FAIL_FLAGS("//", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + PARSE_FAIL_FLAGS("// test", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + PARSE_FAIL_FLAGS("//\n", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + + /* Invalid character following slash */ + PARSE_FAIL_FLAGS("[0/x", SPDK_JSON_PARSE_INVALID, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); + + /* Single slash at end of buffer */ + PARSE_FAIL_FLAGS("[0/", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); +} + +int main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + CU_set_error_action(CUEA_ABORT); + CU_initialize_registry(); + + suite = CU_add_suite("json", NULL, NULL); + + CU_ADD_TEST(suite, test_parse_literal); + CU_ADD_TEST(suite, test_parse_string_simple); + CU_ADD_TEST(suite, test_parse_string_control_chars); + CU_ADD_TEST(suite, test_parse_string_utf8); + CU_ADD_TEST(suite, test_parse_string_escapes_twochar); + CU_ADD_TEST(suite, test_parse_string_escapes_unicode); + CU_ADD_TEST(suite, test_parse_number); + CU_ADD_TEST(suite, test_parse_array); + CU_ADD_TEST(suite, test_parse_object); + CU_ADD_TEST(suite, test_parse_nesting); + CU_ADD_TEST(suite, test_parse_comment); + + CU_basic_set_mode(CU_BRM_VERBOSE); + + CU_basic_run_tests(); + + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} diff --git a/src/spdk/test/unit/lib/json/json_util.c/.gitignore b/src/spdk/test/unit/lib/json/json_util.c/.gitignore new file mode 100644 index 000000000..02f6d50c5 --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_util.c/.gitignore @@ -0,0 +1 @@ +json_util_ut diff --git a/src/spdk/test/unit/lib/json/json_util.c/Makefile b/src/spdk/test/unit/lib/json/json_util.c/Makefile new file mode 100644 index 000000000..c9a282083 --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_util.c/Makefile @@ -0,0 +1,38 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) + +TEST_FILE = json_util_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/json/json_util.c/json_util_ut.c b/src/spdk/test/unit/lib/json/json_util.c/json_util_ut.c new file mode 100644 index 000000000..2f883521f --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_util.c/json_util_ut.c @@ -0,0 +1,954 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "spdk_cunit.h" + +#include "json/json_util.c" + +/* For spdk_json_parse() */ +#include "json/json_parse.c" + +#define NUM_SETUP(x) \ + snprintf(buf, sizeof(buf), "%s", x); \ + v.type = SPDK_JSON_VAL_NUMBER; \ + v.start = buf; \ + v.len = sizeof(x) - 1 + +#define NUM_UINT16_PASS(s, i) \ + NUM_SETUP(s); \ + CU_ASSERT(spdk_json_number_to_uint16(&v, &u16) == 0); \ + CU_ASSERT(u16 == i) + +#define NUM_UINT16_FAIL(s) \ + NUM_SETUP(s); \ + CU_ASSERT(spdk_json_number_to_uint16(&v, &u16) != 0) + +#define NUM_INT32_PASS(s, i) \ + NUM_SETUP(s); \ + CU_ASSERT(spdk_json_number_to_int32(&v, &i32) == 0); \ + CU_ASSERT(i32 == i) + +#define NUM_INT32_FAIL(s) \ + NUM_SETUP(s); \ + CU_ASSERT(spdk_json_number_to_int32(&v, &i32) != 0) + +#define NUM_UINT64_PASS(s, i) \ + NUM_SETUP(s); \ + CU_ASSERT(spdk_json_number_to_uint64(&v, &u64) == 0); \ + CU_ASSERT(u64 == i) + +#define NUM_UINT64_FAIL(s) \ + NUM_SETUP(s); \ + CU_ASSERT(spdk_json_number_to_uint64(&v, &u64) != 0) + +static void +test_strequal(void) +{ + struct spdk_json_val v; + + v.type = SPDK_JSON_VAL_STRING; + v.start = "test"; + v.len = sizeof("test") - 1; + CU_ASSERT(spdk_json_strequal(&v, "test") == true); + CU_ASSERT(spdk_json_strequal(&v, "TEST") == false); + CU_ASSERT(spdk_json_strequal(&v, "hello") == false); + CU_ASSERT(spdk_json_strequal(&v, "t") == false); + + v.type = SPDK_JSON_VAL_NAME; + CU_ASSERT(spdk_json_strequal(&v, "test") == true); + + v.type = SPDK_JSON_VAL_NUMBER; + CU_ASSERT(spdk_json_strequal(&v, "test") == false); + + v.type = SPDK_JSON_VAL_STRING; + v.start = "test\0hello"; + v.len = sizeof("test\0hello") - 1; + CU_ASSERT(spdk_json_strequal(&v, "test") == false); +} + +static void +test_num_to_uint16(void) +{ + struct spdk_json_val v; + char buf[100]; + uint16_t u16 = 0; + + NUM_SETUP("1234"); + CU_ASSERT(spdk_json_number_to_uint16(&v, &u16) == 0); + CU_ASSERT(u16 == 1234); + + NUM_UINT16_PASS("0", 0); + NUM_UINT16_PASS("1234", 1234); + NUM_UINT16_PASS("1234.00000", 1234); + NUM_UINT16_PASS("1.2e1", 12); + NUM_UINT16_PASS("12340e-1", 1234); + + NUM_UINT16_FAIL("1.2"); + NUM_UINT16_FAIL("-1234"); + NUM_UINT16_FAIL("1.2E0"); + NUM_UINT16_FAIL("1.234e1"); + NUM_UINT16_FAIL("12341e-1"); +} + +static void +test_num_to_int32(void) +{ + struct spdk_json_val v; + char buf[100]; + int32_t i32 = 0; + + NUM_SETUP("1234"); + CU_ASSERT(spdk_json_number_to_int32(&v, &i32) == 0); + CU_ASSERT(i32 == 1234); + + + NUM_INT32_PASS("0", 0); + NUM_INT32_PASS("1234", 1234); + NUM_INT32_PASS("-1234", -1234); + NUM_INT32_PASS("1234.00000", 1234); + NUM_INT32_PASS("1.2e1", 12); + NUM_INT32_PASS("12340e-1", 1234); + NUM_INT32_PASS("-0", 0); + + NUM_INT32_FAIL("1.2"); + NUM_INT32_FAIL("1.2E0"); + NUM_INT32_FAIL("1.234e1"); + NUM_INT32_FAIL("12341e-1"); +} + +static void +test_num_to_uint64(void) +{ + struct spdk_json_val v; + char buf[100]; + uint64_t u64 = 0; + + NUM_SETUP("1234"); + CU_ASSERT(spdk_json_number_to_uint64(&v, &u64) == 0); + CU_ASSERT(u64 == 1234); + + + NUM_UINT64_PASS("0", 0); + NUM_UINT64_PASS("1234", 1234); + NUM_UINT64_PASS("1234.00000", 1234); + NUM_UINT64_PASS("1.2e1", 12); + NUM_UINT64_PASS("12340e-1", 1234); + NUM_UINT64_PASS("123456780e-1", 12345678); + + NUM_UINT64_FAIL("1.2"); + NUM_UINT64_FAIL("-1234"); + NUM_UINT64_FAIL("1.2E0"); + NUM_UINT64_FAIL("1.234e1"); + NUM_UINT64_FAIL("12341e-1"); + NUM_UINT64_FAIL("123456781e-1"); +} + +static void +test_decode_object(void) +{ + struct my_object { + char *my_name; + uint32_t my_int; + bool my_bool; + }; + struct spdk_json_val object[] = { + {"", 6, SPDK_JSON_VAL_OBJECT_BEGIN}, + {"first", 5, SPDK_JSON_VAL_NAME}, + {"HELLO", 5, SPDK_JSON_VAL_STRING}, + {"second", 6, SPDK_JSON_VAL_NAME}, + {"234", 3, SPDK_JSON_VAL_NUMBER}, + {"third", 5, SPDK_JSON_VAL_NAME}, + {"", 1, SPDK_JSON_VAL_TRUE}, + {"", 0, SPDK_JSON_VAL_OBJECT_END}, + }; + + struct spdk_json_object_decoder decoders[] = { + {"first", offsetof(struct my_object, my_name), spdk_json_decode_string, false}, + {"second", offsetof(struct my_object, my_int), spdk_json_decode_uint32, false}, + {"third", offsetof(struct my_object, my_bool), spdk_json_decode_bool, false}, + {"fourth", offsetof(struct my_object, my_bool), spdk_json_decode_bool, true}, + }; + struct my_object output = { + .my_name = NULL, + .my_int = 0, + .my_bool = false, + }; + uint32_t answer = 234; + char *answer_str = "HELLO"; + bool answer_bool = true; + + /* Passing Test: object containing simple types */ + CU_ASSERT(spdk_json_decode_object(object, decoders, 4, &output) == 0); + SPDK_CU_ASSERT_FATAL(output.my_name != NULL); + CU_ASSERT(memcmp(output.my_name, answer_str, 6) == 0); + CU_ASSERT(output.my_int == answer); + CU_ASSERT(output.my_bool == answer_bool); + + /* Failing Test: member with no matching decoder */ + /* i.e. I remove the matching decoder from the boolean argument */ + CU_ASSERT(spdk_json_decode_object(object, decoders, 2, &output) != 0); + + /* Failing Test: non-optional decoder with no corresponding member */ + + decoders[3].optional = false; + CU_ASSERT(spdk_json_decode_object(object, decoders, 4, &output) != 0); + + /* return to base state */ + decoders[3].optional = true; + + /* Failing Test: duplicated names for json values */ + object[3].start = "first"; + object[3].len = 5; + CU_ASSERT(spdk_json_decode_object(object, decoders, 3, &output) != 0); + + /* return to base state */ + object[3].start = "second"; + object[3].len = 6; + + /* Failing Test: invalid value for decoder */ + object[2].start = "HELO"; + CU_ASSERT(spdk_json_decode_object(object, decoders, 3, &output) != 0); + + /* return to base state */ + object[2].start = "HELLO"; + + /* Failing Test: not an object */ + object[0].type = SPDK_JSON_VAL_ARRAY_BEGIN; + CU_ASSERT(spdk_json_decode_object(object, decoders, 3, &output) != 0); + + free(output.my_name); +} + +static void +test_decode_array(void) +{ + struct spdk_json_val values[4]; + uint32_t my_int[2] = {0, 0}; + char *my_string[2] = {NULL, NULL}; + size_t out_size; + + /* passing integer test */ + values[0].type = SPDK_JSON_VAL_ARRAY_BEGIN; + values[0].len = 2; + values[1].type = SPDK_JSON_VAL_NUMBER; + values[1].len = 4; + values[1].start = "1234"; + values[2].type = SPDK_JSON_VAL_NUMBER; + values[2].len = 4; + values[2].start = "5678"; + values[3].type = SPDK_JSON_VAL_ARRAY_END; + CU_ASSERT(spdk_json_decode_array(values, spdk_json_decode_uint32, my_int, 2, &out_size, + sizeof(uint32_t)) == 0); + CU_ASSERT(my_int[0] == 1234); + CU_ASSERT(my_int[1] == 5678); + CU_ASSERT(out_size == 2); + + /* array length exceeds max */ + values[0].len = 3; + CU_ASSERT(spdk_json_decode_array(values, spdk_json_decode_uint32, my_int, 2, &out_size, + sizeof(uint32_t)) != 0); + + /* mixed types */ + values[0].len = 2; + values[2].type = SPDK_JSON_VAL_STRING; + values[2].len = 5; + values[2].start = "HELLO"; + CU_ASSERT(spdk_json_decode_array(values, spdk_json_decode_uint32, my_int, 2, &out_size, + sizeof(uint32_t)) != 0); + + /* no array start */ + values[0].type = SPDK_JSON_VAL_NUMBER; + values[2].type = SPDK_JSON_VAL_NUMBER; + values[2].len = 4; + values[2].start = "5678"; + CU_ASSERT(spdk_json_decode_array(values, spdk_json_decode_uint32, my_int, 2, &out_size, + sizeof(uint32_t)) != 0); + + /* mismatched array type and parser */ + values[0].type = SPDK_JSON_VAL_ARRAY_BEGIN; + values[1].type = SPDK_JSON_VAL_STRING; + values[1].len = 5; + values[1].start = "HELLO"; + values[2].type = SPDK_JSON_VAL_STRING; + values[2].len = 5; + values[2].start = "WORLD"; + CU_ASSERT(spdk_json_decode_array(values, spdk_json_decode_uint32, my_int, 2, &out_size, + sizeof(uint32_t)) != 0); + + /* passing String example */ + CU_ASSERT(spdk_json_decode_array(values, spdk_json_decode_string, my_string, 2, &out_size, + sizeof(char *)) == 0); + SPDK_CU_ASSERT_FATAL(my_string[0] != NULL); + SPDK_CU_ASSERT_FATAL(my_string[1] != NULL); + CU_ASSERT(memcmp(my_string[0], "HELLO", 6) == 0); + CU_ASSERT(memcmp(my_string[1], "WORLD", 6) == 0); + CU_ASSERT(out_size == 2); + + free(my_string[0]); + free(my_string[1]); +} + +static void +test_decode_bool(void) +{ + struct spdk_json_val v; + bool b; + + /* valid bool (true) */ + v.type = SPDK_JSON_VAL_TRUE; + b = false; + CU_ASSERT(spdk_json_decode_bool(&v, &b) == 0); + CU_ASSERT(b == true); + + /* valid bool (false) */ + v.type = SPDK_JSON_VAL_FALSE; + b = true; + CU_ASSERT(spdk_json_decode_bool(&v, &b) == 0); + CU_ASSERT(b == false); + + /* incorrect type */ + v.type = SPDK_JSON_VAL_NULL; + CU_ASSERT(spdk_json_decode_bool(&v, &b) != 0); +} + +static void +test_decode_int32(void) +{ + struct spdk_json_val v; + int32_t i; + + /* correct type and valid value */ + v.type = SPDK_JSON_VAL_NUMBER; + v.start = "33"; + v.len = 2; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) == 0); + CU_ASSERT(i == 33); + + /* correct type and invalid value (float) */ + v.start = "32.45"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); + + /* incorrect type */ + v.type = SPDK_JSON_VAL_STRING; + v.start = "String"; + v.len = 6; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); + + /* incorrect type */ + v.type = SPDK_JSON_VAL_TRUE; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); + + /* edge case (integer max) */ + v.type = SPDK_JSON_VAL_NUMBER; + v.start = "2147483647"; + v.len = 10; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) == 0); + CU_ASSERT(i == 2147483647); + + /* invalid value (overflow) */ + v.start = "2147483648"; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); + + /* edge case (integer min) */ + v.type = SPDK_JSON_VAL_NUMBER; + v.start = "-2147483648"; + v.len = 11; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) == 0); + CU_ASSERT(i == -2147483648); + + /* invalid value (overflow) */ + v.start = "-2147483649"; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); + + /* valid exponent */ + v.start = "4e3"; + v.len = 3; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) == 0); + CU_ASSERT(i == 4000); + + /* invalid negative exponent */ + v.start = "-400e-4"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); + + /* invalid negative exponent */ + v.start = "400e-4"; + v.len = 6; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); + + /* valid negative exponent */ + v.start = "-400e-2"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) == 0); + CU_ASSERT(i == -4); + + /* invalid exponent (overflow) */ + v.start = "-2e32"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); + + /* valid exponent with decimal */ + v.start = "2.13e2"; + v.len = 6; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) == 0); + CU_ASSERT(i == 213); + + /* invalid exponent with decimal */ + v.start = "2.134e2"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_int32(&v, &i) != 0); +} + +static void +test_decode_uint16(void) +{ + struct spdk_json_val v; + uint32_t i; + + /* incorrect type */ + v.type = SPDK_JSON_VAL_STRING; + v.start = "Strin"; + v.len = 5; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) != 0); + + /* invalid value (float) */ + v.type = SPDK_JSON_VAL_NUMBER; + v.start = "123.4"; + v.len = 5; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) != 0); + + /* edge case (0) */ + v.start = "0"; + v.len = 1; + i = 456; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) == 0); + CU_ASSERT(i == 0); + + /* invalid value (negative) */ + v.start = "-1"; + v.len = 2; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) != 0); + + /* edge case (maximum) */ + v.start = "65535"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) == 0); + CU_ASSERT(i == 65535); + + /* invalid value (overflow) */ + v.start = "65536"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) != 0); + + /* valid exponent */ + v.start = "66E2"; + v.len = 4; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) == 0); + CU_ASSERT(i == 6600); + + /* invalid exponent (overflow) */ + v.start = "66E3"; + v.len = 4; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) != 0); + + /* invalid exponent (decimal) */ + v.start = "65.535E2"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) != 0); + + /* valid exponent with decimal */ + v.start = "65.53E2"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) == 0); + CU_ASSERT(i == 6553); + + /* invalid negative exponent */ + v.start = "40e-2"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) != 0); + + /* invalid negative exponent */ + v.start = "-40e-1"; + v.len = 6; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) != 0); + + /* valid negative exponent */ + v.start = "40e-1"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_uint16(&v, &i) == 0); + CU_ASSERT(i == 4); +} + +static void +test_decode_uint32(void) +{ + struct spdk_json_val v; + uint32_t i; + + /* incorrect type */ + v.type = SPDK_JSON_VAL_STRING; + v.start = "String"; + v.len = 6; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) != 0); + + /* invalid value (float) */ + v.type = SPDK_JSON_VAL_NUMBER; + v.start = "123.45"; + v.len = 6; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) != 0); + + /* edge case (0) */ + v.start = "0"; + v.len = 1; + i = 456; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) == 0); + CU_ASSERT(i == 0); + + /* invalid value (negative) */ + v.start = "-1"; + v.len = 2; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) != 0); + + /* edge case (maximum) */ + v.start = "4294967295"; + v.len = 10; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) == 0); + CU_ASSERT(i == 4294967295); + + /* invalid value (overflow) */ + v.start = "4294967296"; + v.len = 10; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) != 0); + + /* valid exponent */ + v.start = "42E2"; + v.len = 4; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) == 0); + CU_ASSERT(i == 4200); + + /* invalid exponent (overflow) */ + v.start = "42e32"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) != 0); + + /* invalid exponent (decimal) */ + v.start = "42.323E2"; + v.len = 8; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) != 0); + + /* valid exponent with decimal */ + v.start = "42.32E2"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) == 0); + CU_ASSERT(i == 4232); + + /* invalid negative exponent */ + v.start = "400e-4"; + v.len = 6; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) != 0); + + /* invalid negative exponent */ + v.start = "-400e-2"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) != 0); + + /* valid negative exponent */ + v.start = "400e-2"; + v.len = 6; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) == 0); + CU_ASSERT(i == 4); + + /* valid negative exponent */ + v.start = "10e-1"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_uint32(&v, &i) == 0); + CU_ASSERT(i == 1); +} + +static void +test_decode_uint64(void) +{ + struct spdk_json_val v; + uint64_t i; + + /* incorrect type */ + v.type = SPDK_JSON_VAL_STRING; + v.start = "String"; + v.len = 6; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) != 0); + + /* invalid value (float) */ + v.type = SPDK_JSON_VAL_NUMBER; + v.start = "123.45"; + v.len = 6; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) != 0); + + /* edge case (0) */ + v.start = "0"; + v.len = 1; + i = 456; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) == 0); + CU_ASSERT(i == 0); + + /* invalid value (negative) */ + v.start = "-1"; + v.len = 2; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) != 0); + + /* edge case (maximum) */ + v.start = "18446744073709551615"; + v.len = 20; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) == 0); + CU_ASSERT(i == 18446744073709551615U); + + /* invalid value (overflow) */ + v.start = "18446744073709551616"; + v.len = 20; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) != 0); + + /* valid exponent */ + v.start = "42E2"; + v.len = 4; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) == 0); + CU_ASSERT(i == 4200); + + /* invalid exponent (overflow) */ + v.start = "42e64"; + v.len = 5; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) != 0); + + /* invalid exponent (decimal) */ + v.start = "42.323E2"; + v.len = 8; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) != 0); + + /* valid exponent with decimal */ + v.start = "42.32E2"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) == 0); + CU_ASSERT(i == 4232); + + /* invalid negative exponent */ + v.start = "400e-4"; + v.len = 6; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) != 0); + + /* invalid negative exponent */ + v.start = "-400e-2"; + v.len = 7; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) != 0); + + /* valid negative exponent */ + v.start = "400e-2"; + v.len = 6; + i = 0; + CU_ASSERT(spdk_json_decode_uint64(&v, &i) == 0); + CU_ASSERT(i == 4); +} + +static void +test_decode_string(void) +{ + struct spdk_json_val v; + char *value = NULL; + + /* Passing Test: Standard */ + v.type = SPDK_JSON_VAL_STRING; + v.start = "HELLO"; + v.len = 5; + CU_ASSERT(spdk_json_decode_string(&v, &value) == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(memcmp(value, v.start, 6) == 0); + + /* Edge Test: Empty String */ + v.start = ""; + v.len = 0; + CU_ASSERT(spdk_json_decode_string(&v, &value) == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(memcmp(value, v.start, 1) == 0); + + /* + * Failing Test: Null Terminator In String + * It is valid for a json string to contain \u0000 and the parser will accept it. + * However, a null terminated C string cannot contain '\0' and should be rejected + * if that character is found before the end of the string. + */ + v.start = "HELO"; + v.len = 5; + CU_ASSERT(spdk_json_decode_string(&v, &value) != 0); + + /* Failing Test: Wrong Type */ + v.start = "45673"; + v.type = SPDK_JSON_VAL_NUMBER; + CU_ASSERT(spdk_json_decode_string(&v, &value) != 0); + + /* Passing Test: Special Characters */ + v.type = SPDK_JSON_VAL_STRING; + v.start = "HE\bLL\tO\\WORLD"; + v.len = 13; + CU_ASSERT(spdk_json_decode_string(&v, &value) == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(memcmp(value, v.start, 14) == 0); + + free(value); +} + +char ut_json_text[] = + "{" + " \"string\": \"Some string data\"," + " \"object\": { " + " \"another_string\": \"Yet anoter string data\"," + " \"array name with space\": [1, [], {} ]" + " }," + " \"array\": [ \"Text\", 2, {} ]" + "}" + ; + +static void +test_find(void) +{ + struct spdk_json_val *values, *key, *val, *key2, *val2; + ssize_t values_cnt; + ssize_t rc; + + values_cnt = spdk_json_parse(ut_json_text, strlen(ut_json_text), NULL, 0, NULL, 0); + SPDK_CU_ASSERT_FATAL(values_cnt > 0); + + values = calloc(values_cnt, sizeof(struct spdk_json_val)); + SPDK_CU_ASSERT_FATAL(values != NULL); + + rc = spdk_json_parse(ut_json_text, strlen(ut_json_text), values, values_cnt, NULL, 0); + SPDK_CU_ASSERT_FATAL(values_cnt == rc); + + key = val = NULL; + rc = spdk_json_find(values, "string", &key, &val, SPDK_JSON_VAL_STRING); + CU_ASSERT(rc == 0); + + CU_ASSERT(key != NULL && spdk_json_strequal(key, "string") == true); + CU_ASSERT(val != NULL && spdk_json_strequal(val, "Some string data") == true); + + key = val = NULL; + rc = spdk_json_find(values, "object", &key, &val, SPDK_JSON_VAL_OBJECT_BEGIN); + CU_ASSERT(rc == 0); + + CU_ASSERT(key != NULL && spdk_json_strequal(key, "object") == true); + + /* Find key in "object" by passing SPDK_JSON_VAL_ANY to match any type */ + key2 = val2 = NULL; + rc = spdk_json_find(val, "array name with space", &key2, &val2, SPDK_JSON_VAL_ANY); + CU_ASSERT(rc == 0); + CU_ASSERT(key2 != NULL && spdk_json_strequal(key2, "array name with space") == true); + CU_ASSERT(val2 != NULL && val2->type == SPDK_JSON_VAL_ARRAY_BEGIN); + + /* Find the "array" key in "object" by passing SPDK_JSON_VAL_ARRAY_BEGIN to match only array */ + key2 = val2 = NULL; + rc = spdk_json_find(val, "array name with space", &key2, &val2, SPDK_JSON_VAL_ARRAY_BEGIN); + CU_ASSERT(rc == 0); + CU_ASSERT(key2 != NULL && spdk_json_strequal(key2, "array name with space") == true); + CU_ASSERT(val2 != NULL && val2->type == SPDK_JSON_VAL_ARRAY_BEGIN); + + /* Negative test - key doesn't exist */ + key2 = val2 = NULL; + rc = spdk_json_find(val, "this_key_does_not_exist", &key2, &val2, SPDK_JSON_VAL_ANY); + CU_ASSERT(rc == -ENOENT); + + /* Negative test - key type doesn't match */ + key2 = val2 = NULL; + rc = spdk_json_find(val, "another_string", &key2, &val2, SPDK_JSON_VAL_ARRAY_BEGIN); + CU_ASSERT(rc == -EDOM); + + free(values); +} + +static void +test_iterating(void) +{ + struct spdk_json_val *values; + struct spdk_json_val *string_key; + struct spdk_json_val *object_key, *object_val; + struct spdk_json_val *array_key, *array_val; + struct spdk_json_val *another_string_key; + struct spdk_json_val *array_name_with_space_key, *array_name_with_space_val; + struct spdk_json_val *it; + ssize_t values_cnt; + ssize_t rc; + + values_cnt = spdk_json_parse(ut_json_text, strlen(ut_json_text), NULL, 0, NULL, 0); + SPDK_CU_ASSERT_FATAL(values_cnt > 0); + + values = calloc(values_cnt, sizeof(struct spdk_json_val)); + SPDK_CU_ASSERT_FATAL(values != NULL); + + rc = spdk_json_parse(ut_json_text, strlen(ut_json_text), values, values_cnt, NULL, 0); + SPDK_CU_ASSERT_FATAL(values_cnt == rc); + + /* Iterate over object keys. JSON spec doesn't guarantee order of keys in object but + * SPDK implementation implicitly does. + */ + string_key = spdk_json_object_first(values); + CU_ASSERT(spdk_json_strequal(string_key, "string") == true); + + object_key = spdk_json_next(string_key); + object_val = json_value(object_key); + CU_ASSERT(spdk_json_strequal(object_key, "object") == true); + + array_key = spdk_json_next(object_key); + array_val = json_value(array_key); + CU_ASSERT(spdk_json_strequal(array_key, "array") == true); + + /* NULL '}' */ + CU_ASSERT(spdk_json_next(array_key) == NULL); + + /* Iterate over subobjects */ + another_string_key = spdk_json_object_first(object_val); + CU_ASSERT(spdk_json_strequal(another_string_key, "another_string") == true); + + array_name_with_space_key = spdk_json_next(another_string_key); + array_name_with_space_val = json_value(array_name_with_space_key); + CU_ASSERT(spdk_json_strequal(array_name_with_space_key, "array name with space") == true); + + CU_ASSERT(spdk_json_next(array_name_with_space_key) == NULL); + + /* Iterate over array in subobject */ + it = spdk_json_array_first(array_name_with_space_val); + SPDK_CU_ASSERT_FATAL(it != NULL); + CU_ASSERT(it->type == SPDK_JSON_VAL_NUMBER); + + it = spdk_json_next(it); + SPDK_CU_ASSERT_FATAL(it != NULL); + CU_ASSERT(it->type == SPDK_JSON_VAL_ARRAY_BEGIN); + + it = spdk_json_next(it); + SPDK_CU_ASSERT_FATAL(it != NULL); + CU_ASSERT(it->type == SPDK_JSON_VAL_OBJECT_BEGIN); + + it = spdk_json_next(it); + CU_ASSERT(it == NULL); + + /* Iterate over array in root object */ + it = spdk_json_array_first(array_val); + SPDK_CU_ASSERT_FATAL(it != NULL); + CU_ASSERT(it->type == SPDK_JSON_VAL_STRING); + + it = spdk_json_next(it); + SPDK_CU_ASSERT_FATAL(it != NULL); + CU_ASSERT(it->type == SPDK_JSON_VAL_NUMBER); + + it = spdk_json_next(it); + SPDK_CU_ASSERT_FATAL(it != NULL); + CU_ASSERT(it->type == SPDK_JSON_VAL_OBJECT_BEGIN); + + /* Array end */ + it = spdk_json_next(it); + CU_ASSERT(it == NULL); + + free(values); +} + +int main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + CU_set_error_action(CUEA_ABORT); + CU_initialize_registry(); + + suite = CU_add_suite("json", NULL, NULL); + + CU_ADD_TEST(suite, test_strequal); + CU_ADD_TEST(suite, test_num_to_uint16); + CU_ADD_TEST(suite, test_num_to_int32); + CU_ADD_TEST(suite, test_num_to_uint64); + CU_ADD_TEST(suite, test_decode_object); + CU_ADD_TEST(suite, test_decode_array); + CU_ADD_TEST(suite, test_decode_bool); + CU_ADD_TEST(suite, test_decode_uint16); + CU_ADD_TEST(suite, test_decode_int32); + CU_ADD_TEST(suite, test_decode_uint32); + CU_ADD_TEST(suite, test_decode_uint64); + CU_ADD_TEST(suite, test_decode_string); + CU_ADD_TEST(suite, test_find); + CU_ADD_TEST(suite, test_iterating); + + CU_basic_set_mode(CU_BRM_VERBOSE); + + CU_basic_run_tests(); + + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} diff --git a/src/spdk/test/unit/lib/json/json_write.c/.gitignore b/src/spdk/test/unit/lib/json/json_write.c/.gitignore new file mode 100644 index 000000000..dd576b238 --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_write.c/.gitignore @@ -0,0 +1 @@ +json_write_ut diff --git a/src/spdk/test/unit/lib/json/json_write.c/Makefile b/src/spdk/test/unit/lib/json/json_write.c/Makefile new file mode 100644 index 000000000..9fe1fa916 --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_write.c/Makefile @@ -0,0 +1,38 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) + +TEST_FILE = json_write_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/json/json_write.c/json_write_ut.c b/src/spdk/test/unit/lib/json/json_write.c/json_write_ut.c new file mode 100644 index 000000000..d208f650c --- /dev/null +++ b/src/spdk/test/unit/lib/json/json_write.c/json_write_ut.c @@ -0,0 +1,736 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "spdk_cunit.h" + +#include "json/json_write.c" +#include "json/json_parse.c" + +#include "spdk/util.h" + +static uint8_t g_buf[1000]; +static uint8_t *g_write_pos; + +static int +write_cb(void *cb_ctx, const void *data, size_t size) +{ + size_t buf_free = g_buf + sizeof(g_buf) - g_write_pos; + + if (size > buf_free) { + return -1; + } + + memcpy(g_write_pos, data, size); + g_write_pos += size; + + return 0; +} + +#define BEGIN() \ + memset(g_buf, 0, sizeof(g_buf)); \ + g_write_pos = g_buf; \ + w = spdk_json_write_begin(write_cb, NULL, 0); \ + SPDK_CU_ASSERT_FATAL(w != NULL) + +#define END(json) \ + CU_ASSERT(spdk_json_write_end(w) == 0); \ + CU_ASSERT(g_write_pos - g_buf == sizeof(json) - 1); \ + CU_ASSERT(memcmp(json, g_buf, sizeof(json) - 1) == 0) + +#define END_NOCMP() \ + CU_ASSERT(spdk_json_write_end(w) == 0) + +#define END_FAIL() \ + CU_ASSERT(spdk_json_write_end(w) < 0) + +#define VAL_STRING(str) \ + CU_ASSERT(spdk_json_write_string_raw(w, str, sizeof(str) - 1) == 0) + +#define VAL_STRING_FAIL(str) \ + CU_ASSERT(spdk_json_write_string_raw(w, str, sizeof(str) - 1) < 0) + +#define STR_PASS(in, out) \ + BEGIN(); VAL_STRING(in); END("\"" out "\"") + +#define STR_FAIL(in) \ + BEGIN(); VAL_STRING_FAIL(in); END_FAIL() + +#define VAL_STRING_UTF16LE(str) \ + CU_ASSERT(spdk_json_write_string_utf16le_raw(w, (const uint16_t *)str, sizeof(str) / sizeof(uint16_t) - 1) == 0) + +#define VAL_STRING_UTF16LE_FAIL(str) \ + CU_ASSERT(spdk_json_write_string_utf16le_raw(w, (const uint16_t *)str, sizeof(str) / sizeof(uint16_t) - 1) < 0) + +#define STR_UTF16LE_PASS(in, out) \ + BEGIN(); VAL_STRING_UTF16LE(in); END("\"" out "\"") + +#define STR_UTF16LE_FAIL(in) \ + BEGIN(); VAL_STRING_UTF16LE_FAIL(in); END_FAIL() + +#define VAL_NAME(name) \ + CU_ASSERT(spdk_json_write_name_raw(w, name, sizeof(name) - 1) == 0) + +#define VAL_NULL() CU_ASSERT(spdk_json_write_null(w) == 0) +#define VAL_TRUE() CU_ASSERT(spdk_json_write_bool(w, true) == 0) +#define VAL_FALSE() CU_ASSERT(spdk_json_write_bool(w, false) == 0) + +#define VAL_INT32(i) CU_ASSERT(spdk_json_write_int32(w, i) == 0); +#define VAL_UINT32(u) CU_ASSERT(spdk_json_write_uint32(w, u) == 0); + +#define VAL_INT64(i) CU_ASSERT(spdk_json_write_int64(w, i) == 0); +#define VAL_UINT64(u) CU_ASSERT(spdk_json_write_uint64(w, u) == 0); + +#define VAL_ARRAY_BEGIN() CU_ASSERT(spdk_json_write_array_begin(w) == 0) +#define VAL_ARRAY_END() CU_ASSERT(spdk_json_write_array_end(w) == 0) + +#define VAL_OBJECT_BEGIN() CU_ASSERT(spdk_json_write_object_begin(w) == 0) +#define VAL_OBJECT_END() CU_ASSERT(spdk_json_write_object_end(w) == 0) + +#define VAL(v) CU_ASSERT(spdk_json_write_val(w, v) == 0) + +static void +test_write_literal(void) +{ + struct spdk_json_write_ctx *w; + + BEGIN(); + VAL_NULL(); + END("null"); + + BEGIN(); + VAL_TRUE(); + END("true"); + + BEGIN(); + VAL_FALSE(); + END("false"); +} + +static void +test_write_string_simple(void) +{ + struct spdk_json_write_ctx *w; + + STR_PASS("hello world", "hello world"); + STR_PASS(" ", " "); + STR_PASS("~", "~"); +} + +static void +test_write_string_escapes(void) +{ + struct spdk_json_write_ctx *w; + + /* Two-character escapes */ + STR_PASS("\b", "\\b"); + STR_PASS("\f", "\\f"); + STR_PASS("\n", "\\n"); + STR_PASS("\r", "\\r"); + STR_PASS("\t", "\\t"); + STR_PASS("\"", "\\\""); + STR_PASS("\\", "\\\\"); + + /* JSON defines an escape for forward slash, but it is optional */ + STR_PASS("/", "/"); + + STR_PASS("hello\nworld", "hello\\nworld"); + + STR_PASS("\x00", "\\u0000"); + STR_PASS("\x01", "\\u0001"); + STR_PASS("\x02", "\\u0002"); + + STR_PASS("\xC3\xB6", "\\u00F6"); + STR_PASS("\xE2\x88\x9A", "\\u221A"); + STR_PASS("\xEA\xAA\xAA", "\\uAAAA"); + + /* Surrogate pairs */ + STR_PASS("\xF0\x9D\x84\x9E", "\\uD834\\uDD1E"); + STR_PASS("\xF0\xA0\x9C\x8E", "\\uD841\\uDF0E"); + + /* Examples from RFC 3629 */ + STR_PASS("\x41\xE2\x89\xA2\xCE\x91\x2E", "A\\u2262\\u0391."); + STR_PASS("\xED\x95\x9C\xEA\xB5\xAD\xEC\x96\xB4", "\\uD55C\\uAD6D\\uC5B4"); + STR_PASS("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E", "\\u65E5\\u672C\\u8A9E"); + STR_PASS("\xEF\xBB\xBF\xF0\xA3\x8E\xB4", "\\uFEFF\\uD84C\\uDFB4"); + + /* UTF-8 edge cases */ + STR_PASS("\x7F", "\\u007F"); + STR_FAIL("\x80"); + STR_FAIL("\xC1"); + STR_FAIL("\xC2"); + STR_PASS("\xC2\x80", "\\u0080"); + STR_PASS("\xC2\xBF", "\\u00BF"); + STR_PASS("\xDF\x80", "\\u07C0"); + STR_PASS("\xDF\xBF", "\\u07FF"); + STR_FAIL("\xDF"); + STR_FAIL("\xE0\x80"); + STR_FAIL("\xE0\x1F"); + STR_FAIL("\xE0\x1F\x80"); + STR_FAIL("\xE0"); + STR_FAIL("\xE0\xA0"); + STR_PASS("\xE0\xA0\x80", "\\u0800"); + STR_PASS("\xE0\xA0\xBF", "\\u083F"); + STR_FAIL("\xE0\xA0\xC0"); + STR_PASS("\xE0\xBF\x80", "\\u0FC0"); + STR_PASS("\xE0\xBF\xBF", "\\u0FFF"); + STR_FAIL("\xE0\xC0\x80"); + STR_FAIL("\xE1"); + STR_FAIL("\xE1\x80"); + STR_FAIL("\xE1\x7F\x80"); + STR_FAIL("\xE1\x80\x7F"); + STR_PASS("\xE1\x80\x80", "\\u1000"); + STR_PASS("\xE1\x80\xBF", "\\u103F"); + STR_PASS("\xE1\xBF\x80", "\\u1FC0"); + STR_PASS("\xE1\xBF\xBF", "\\u1FFF"); + STR_FAIL("\xE1\xC0\x80"); + STR_FAIL("\xE1\x80\xC0"); + STR_PASS("\xEF\x80\x80", "\\uF000"); + STR_PASS("\xEF\xBF\xBF", "\\uFFFF"); + STR_FAIL("\xF0"); + STR_FAIL("\xF0\x90"); + STR_FAIL("\xF0\x90\x80"); + STR_FAIL("\xF0\x80\x80\x80"); + STR_FAIL("\xF0\x8F\x80\x80"); + STR_PASS("\xF0\x90\x80\x80", "\\uD800\\uDC00"); + STR_PASS("\xF0\x90\x80\xBF", "\\uD800\\uDC3F"); + STR_PASS("\xF0\x90\xBF\x80", "\\uD803\\uDFC0"); + STR_PASS("\xF0\xBF\x80\x80", "\\uD8BC\\uDC00"); + STR_FAIL("\xF0\xC0\x80\x80"); + STR_FAIL("\xF1"); + STR_FAIL("\xF1\x80"); + STR_FAIL("\xF1\x80\x80"); + STR_FAIL("\xF1\x80\x80\x7F"); + STR_PASS("\xF1\x80\x80\x80", "\\uD8C0\\uDC00"); + STR_PASS("\xF1\x80\x80\xBF", "\\uD8C0\\uDC3F"); + STR_PASS("\xF1\x80\xBF\x80", "\\uD8C3\\uDFC0"); + STR_PASS("\xF1\xBF\x80\x80", "\\uD9BC\\uDC00"); + STR_PASS("\xF3\x80\x80\x80", "\\uDAC0\\uDC00"); + STR_FAIL("\xF3\xC0\x80\x80"); + STR_FAIL("\xF3\x80\xC0\x80"); + STR_FAIL("\xF3\x80\x80\xC0"); + STR_FAIL("\xF4"); + STR_FAIL("\xF4\x80"); + STR_FAIL("\xF4\x80\x80"); + STR_PASS("\xF4\x80\x80\x80", "\\uDBC0\\uDC00"); + STR_PASS("\xF4\x8F\x80\x80", "\\uDBFC\\uDC00"); + STR_PASS("\xF4\x8F\xBF\xBF", "\\uDBFF\\uDFFF"); + STR_FAIL("\xF4\x90\x80\x80"); + STR_FAIL("\xF5"); + STR_FAIL("\xF5\x80"); + STR_FAIL("\xF5\x80\x80"); + STR_FAIL("\xF5\x80\x80\x80"); + STR_FAIL("\xF5\x80\x80\x80\x80"); + + /* Overlong encodings */ + STR_FAIL("\xC0\x80"); + + /* Surrogate pairs */ + STR_FAIL("\xED\xA0\x80"); /* U+D800 First high surrogate */ + STR_FAIL("\xED\xAF\xBF"); /* U+DBFF Last high surrogate */ + STR_FAIL("\xED\xB0\x80"); /* U+DC00 First low surrogate */ + STR_FAIL("\xED\xBF\xBF"); /* U+DFFF Last low surrogate */ + STR_FAIL("\xED\xA1\x8C\xED\xBE\xB4"); /* U+233B4 (invalid surrogate pair encoding) */ +} + +static void +test_write_string_utf16le(void) +{ + struct spdk_json_write_ctx *w; + + /* All characters in BMP */ + STR_UTF16LE_PASS(((uint8_t[]) { + 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0, 0x15, 0xFE, 0, 0 + }), "Hello\\uFE15"); + + /* Surrogate pair */ + STR_UTF16LE_PASS(((uint8_t[]) { + 'H', 0, 'i', 0, 0x34, 0xD8, 0x1E, 0xDD, '!', 0, 0, 0 + }), "Hi\\uD834\\uDD1E!"); + + /* Valid high surrogate, but no low surrogate */ + STR_UTF16LE_FAIL(((uint8_t[]) { + 0x00, 0xD8, 0, 0 /* U+D800 */ + })); + + /* Invalid leading low surrogate */ + STR_UTF16LE_FAIL(((uint8_t[]) { + 0x00, 0xDC, 0x00, 0xDC, 0, 0 /* U+DC00 U+DC00 */ + })); + + /* Valid high surrogate followed by another high surrogate (invalid) */ + STR_UTF16LE_FAIL(((uint8_t[]) { + 0x00, 0xD8, 0x00, 0xD8, 0, 0 /* U+D800 U+D800 */ + })); +} + +static void +test_write_number_int32(void) +{ + struct spdk_json_write_ctx *w; + + BEGIN(); + VAL_INT32(0); + END("0"); + + BEGIN(); + VAL_INT32(1); + END("1"); + + BEGIN(); + VAL_INT32(123); + END("123"); + + BEGIN(); + VAL_INT32(-123); + END("-123"); + + BEGIN(); + VAL_INT32(2147483647); + END("2147483647"); + + BEGIN(); + VAL_INT32(-2147483648); + END("-2147483648"); +} + +static void +test_write_number_uint32(void) +{ + struct spdk_json_write_ctx *w; + + BEGIN(); + VAL_UINT32(0); + END("0"); + + BEGIN(); + VAL_UINT32(1); + END("1"); + + BEGIN(); + VAL_UINT32(123); + END("123"); + + BEGIN(); + VAL_UINT32(2147483647); + END("2147483647"); + + BEGIN(); + VAL_UINT32(4294967295); + END("4294967295"); +} + +static void +test_write_number_int64(void) +{ + struct spdk_json_write_ctx *w; + + BEGIN(); + VAL_INT64(0); + END("0"); + + BEGIN(); + VAL_INT64(1); + END("1"); + + BEGIN(); + VAL_INT64(123); + END("123"); + + BEGIN(); + VAL_INT64(-123); + END("-123"); + + BEGIN(); + VAL_INT64(INT64_MAX); + END("9223372036854775807"); + + BEGIN(); + VAL_INT64(INT64_MIN); + END("-9223372036854775808"); +} + +static void +test_write_number_uint64(void) +{ + struct spdk_json_write_ctx *w; + + BEGIN(); + VAL_UINT64(0); + END("0"); + + BEGIN(); + VAL_UINT64(1); + END("1"); + + BEGIN(); + VAL_UINT64(123); + END("123"); + + BEGIN(); + VAL_UINT64(INT64_MAX); + END("9223372036854775807"); + + BEGIN(); + VAL_UINT64(UINT64_MAX); + END("18446744073709551615"); +} + +static void +test_write_array(void) +{ + struct spdk_json_write_ctx *w; + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_END(); + END("[]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_INT32(0); + VAL_ARRAY_END(); + END("[0]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_INT32(0); + VAL_INT32(1); + VAL_ARRAY_END(); + END("[0,1]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_INT32(0); + VAL_INT32(1); + VAL_INT32(2); + VAL_ARRAY_END(); + END("[0,1,2]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_STRING("a"); + VAL_ARRAY_END(); + END("[\"a\"]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_STRING("a"); + VAL_STRING("b"); + VAL_ARRAY_END(); + END("[\"a\",\"b\"]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_STRING("a"); + VAL_STRING("b"); + VAL_STRING("c"); + VAL_ARRAY_END(); + END("[\"a\",\"b\",\"c\"]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_TRUE(); + VAL_ARRAY_END(); + END("[true]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_TRUE(); + VAL_FALSE(); + VAL_ARRAY_END(); + END("[true,false]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_TRUE(); + VAL_FALSE(); + VAL_TRUE(); + VAL_ARRAY_END(); + END("[true,false,true]"); +} + +static void +test_write_object(void) +{ + struct spdk_json_write_ctx *w; + + BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_OBJECT_END(); + END("{}"); + + BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("a"); + VAL_INT32(0); + VAL_OBJECT_END(); + END("{\"a\":0}"); + + BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("a"); + VAL_INT32(0); + VAL_NAME("b"); + VAL_INT32(1); + VAL_OBJECT_END(); + END("{\"a\":0,\"b\":1}"); + + BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("a"); + VAL_INT32(0); + VAL_NAME("b"); + VAL_INT32(1); + VAL_NAME("c"); + VAL_INT32(2); + VAL_OBJECT_END(); + END("{\"a\":0,\"b\":1,\"c\":2}"); +} + +static void +test_write_nesting(void) +{ + struct spdk_json_write_ctx *w; + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_END(); + VAL_ARRAY_END(); + END("[[]]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_END(); + VAL_ARRAY_END(); + VAL_ARRAY_END(); + END("[[[]]]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_INT32(0); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_END(); + VAL_ARRAY_END(); + END("[0,[]]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_ARRAY_END(); + VAL_INT32(0); + VAL_ARRAY_END(); + END("[[],0]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_INT32(0); + VAL_ARRAY_BEGIN(); + VAL_INT32(1); + VAL_ARRAY_END(); + VAL_INT32(2); + VAL_ARRAY_END(); + END("[0,[1],2]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_INT32(0); + VAL_INT32(1); + VAL_ARRAY_BEGIN(); + VAL_INT32(2); + VAL_INT32(3); + VAL_ARRAY_END(); + VAL_INT32(4); + VAL_INT32(5); + VAL_ARRAY_END(); + END("[0,1,[2,3],4,5]"); + + BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("a"); + VAL_OBJECT_BEGIN(); + VAL_OBJECT_END(); + VAL_OBJECT_END(); + END("{\"a\":{}}"); + + BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("a"); + VAL_OBJECT_BEGIN(); + VAL_NAME("b"); + VAL_INT32(0); + VAL_OBJECT_END(); + VAL_OBJECT_END(); + END("{\"a\":{\"b\":0}}"); + + BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("a"); + VAL_ARRAY_BEGIN(); + VAL_INT32(0); + VAL_ARRAY_END(); + VAL_OBJECT_END(); + END("{\"a\":[0]}"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("a"); + VAL_INT32(0); + VAL_OBJECT_END(); + VAL_ARRAY_END(); + END("[{\"a\":0}]"); + + BEGIN(); + VAL_ARRAY_BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("a"); + VAL_OBJECT_BEGIN(); + VAL_NAME("b"); + VAL_ARRAY_BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("c"); + VAL_INT32(1); + VAL_OBJECT_END(); + VAL_INT32(2); + VAL_ARRAY_END(); + VAL_NAME("d"); + VAL_INT32(3); + VAL_OBJECT_END(); + VAL_NAME("e"); + VAL_INT32(4); + VAL_OBJECT_END(); + VAL_INT32(5); + VAL_ARRAY_END(); + END("[{\"a\":{\"b\":[{\"c\":1},2],\"d\":3},\"e\":4},5]"); + + /* Examples from RFC 7159 */ + BEGIN(); + VAL_OBJECT_BEGIN(); + VAL_NAME("Image"); + VAL_OBJECT_BEGIN(); + VAL_NAME("Width"); + VAL_INT32(800); + VAL_NAME("Height"); + VAL_INT32(600); + VAL_NAME("Title"); + VAL_STRING("View from 15th Floor"); + VAL_NAME("Thumbnail"); + VAL_OBJECT_BEGIN(); + VAL_NAME("Url"); + VAL_STRING("http://www.example.com/image/481989943"); + VAL_NAME("Height"); + VAL_INT32(125); + VAL_NAME("Width"); + VAL_INT32(100); + VAL_OBJECT_END(); + VAL_NAME("Animated"); + VAL_FALSE(); + VAL_NAME("IDs"); + VAL_ARRAY_BEGIN(); + VAL_INT32(116); + VAL_INT32(943); + VAL_INT32(234); + VAL_INT32(38793); + VAL_ARRAY_END(); + VAL_OBJECT_END(); + VAL_OBJECT_END(); + END( + "{\"Image\":" + "{" + "\"Width\":800," + "\"Height\":600," + "\"Title\":\"View from 15th Floor\"," + "\"Thumbnail\":{" + "\"Url\":\"http://www.example.com/image/481989943\"," + "\"Height\":125," + "\"Width\":100" + "}," + "\"Animated\":false," + "\"IDs\":[116,943,234,38793]" + "}" + "}"); +} + +/* Round-trip parse and write test */ +static void +test_write_val(void) +{ + struct spdk_json_write_ctx *w; + struct spdk_json_val values[100]; + char src[] = "{\"a\":[1,2,3],\"b\":{\"c\":\"d\"},\"e\":true,\"f\":false,\"g\":null}"; + + CU_ASSERT(spdk_json_parse(src, strlen(src), values, SPDK_COUNTOF(values), NULL, + SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE) == 19); + + BEGIN(); + VAL(values); + END("{\"a\":[1,2,3],\"b\":{\"c\":\"d\"},\"e\":true,\"f\":false,\"g\":null}"); +} + +int main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + CU_set_error_action(CUEA_ABORT); + CU_initialize_registry(); + + suite = CU_add_suite("json", NULL, NULL); + + CU_ADD_TEST(suite, test_write_literal); + CU_ADD_TEST(suite, test_write_string_simple); + CU_ADD_TEST(suite, test_write_string_escapes); + CU_ADD_TEST(suite, test_write_string_utf16le); + CU_ADD_TEST(suite, test_write_number_int32); + CU_ADD_TEST(suite, test_write_number_uint32); + CU_ADD_TEST(suite, test_write_number_int64); + CU_ADD_TEST(suite, test_write_number_uint64); + CU_ADD_TEST(suite, test_write_array); + CU_ADD_TEST(suite, test_write_object); + CU_ADD_TEST(suite, test_write_nesting); + CU_ADD_TEST(suite, test_write_val); + + CU_basic_set_mode(CU_BRM_VERBOSE); + + CU_basic_run_tests(); + + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} |