test-json-parser.c revision 10972f2a15f5538860fcc1d4adda227d59d2d757
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "test-lib.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "str.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "istream-private.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#include "json-parser.h"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#define TYPE_SKIP 100
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen#define TYPE_STREAM 101
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic const char json_input[] =
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen "{\n"
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen "\t\"key\"\t:\t\t\"string\","
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen " \"key2\" : 1234, \n"
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen "\"key3\":true,"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen "\"key4\":false,"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen "\"skip1\": \"jsifjaisfjiasji\","
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen "\"skip2\": { \"x\":{ \"y\":123}, \"z\":[5,[6],{\"k\":0},3]},"
6ded8819b9002150a95a7615e4f64f091c250464Timo Sirainen "\"key5\":null,"
6ded8819b9002150a95a7615e4f64f091c250464Timo Sirainen "\"key6\": {},"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen "\"key7\": {"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen " \"sub1\":\"value\""
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen "},"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen "\"key8\": {"
2f8da04d700cc23fcd6630226a4866e828b761bdTimo Sirainen " \"sub2\":-12.456,\n"
2f8da04d700cc23fcd6630226a4866e828b761bdTimo Sirainen " \"sub3\":12.456e9,\n"
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen " \"sub4\":0.456e-789"
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen "},"
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen "\"key9\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\uffff\","
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen "\"key10\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\uffff\","
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen "\"key11\": [],"
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen "\"key12\": [ \"foo\" , 5.24,[true],{\"aobj\":[]}]"
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen "}\n";
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic struct {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen enum json_type type;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen const char *value;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen} json_output[] = {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_STRING, "string" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key2" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_NUMBER, "1234" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key3" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_TRUE, "true" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key4" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_FALSE, "false" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "skip1" },
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen { TYPE_SKIP, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "skip2" },
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen { TYPE_SKIP, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key5" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_NULL, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key6" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_END, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key7" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub1" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_STRING, "value" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_END, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key8" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT, NULL },
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub2" },
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen { JSON_TYPE_NUMBER, "-12.456" },
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub3" },
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen { JSON_TYPE_NUMBER, "12.456e9" },
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub4" },
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen { JSON_TYPE_NUMBER, "0.456e-789" },
650441dd8282f8fa6ae58d231c0c34488c3bdb49Timo Sirainen { JSON_TYPE_OBJECT_END, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key9" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_STRING, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key10" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { TYPE_STREAM, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key11" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_ARRAY, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_ARRAY_END, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key12" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_ARRAY, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_STRING, "foo" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_NUMBER, "5.24" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_ARRAY, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_TRUE, "true" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_ARRAY_END, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_KEY, "aobj" },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_ARRAY, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_ARRAY_END, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_OBJECT_END, NULL },
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen { JSON_TYPE_ARRAY_END, NULL }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen};
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic int
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstream_read_value(struct istream **input, const char **value_r)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen{
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen const unsigned char *data;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen size_t size;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen ssize_t ret;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen while ((ret = i_stream_read(*input)) > 0) ;
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen if (ret == 0)
082e82792b8ac33ad42beac510441b37a3c50737Timo Sirainen return 0;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen i_assert(ret == -1);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if ((*input)->stream_errno != 0)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen return -1;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen data = i_stream_get_data(*input, &size);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen *value_r = t_strndup(data, size);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen i_stream_unref(input);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen return 1;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen}
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic void test_json_parser_success(bool full_size)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen{
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct json_parser *parser;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen struct istream *input, *jsoninput = NULL;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen enum json_type type;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen const char *value, *error;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen unsigned int i, pos, json_input_len = strlen(json_input);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen int ret = 0;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_begin(full_size ? "json parser" : "json parser (nonblocking)");
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen input = test_istream_create_data(json_input, json_input_len);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_istream_set_allow_eof(input, FALSE);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen parser = json_parser_init(input);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen i = full_size ? json_input_len : 0;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen for (pos = 0; i <= json_input_len; i++) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_istream_set_size(input, i);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen for (;;) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen value = NULL;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (pos < N_ELEMENTS(json_output) &&
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen json_output[pos].type == (enum json_type)TYPE_SKIP) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen json_parse_skip_next(parser);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen pos++;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen continue;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen } else if (pos == N_ELEMENTS(json_output) ||
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen json_output[pos].type != (enum json_type)TYPE_STREAM) {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen ret = json_parse_next(parser, &type, &value);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen } else {
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen ret = jsoninput != NULL ? 1 :
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen json_parse_next_stream(parser, &jsoninput);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (jsoninput != NULL)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen ret = stream_read_value(&jsoninput, &value);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen type = TYPE_STREAM;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen if (ret <= 0)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen break;
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen i_assert(pos < N_ELEMENTS(json_output));
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_assert(json_output[pos].type == type);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_assert(null_strcmp(json_output[pos].value, value) == 0);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen pos++;
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_assert(ret == 0);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen }
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_assert(pos == N_ELEMENTS(json_output));
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_istream_set_allow_eof(input, TRUE);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_assert(json_parse_next(parser, &type, &value) == -1);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
8fcf84e6b7a55049a6d407b17af50d6ae8c6386fPhil Carmody i_stream_unref(&input);
8fcf84e6b7a55049a6d407b17af50d6ae8c6386fPhil Carmody test_assert(json_parser_deinit(&parser, &error) == 0);
8fcf84e6b7a55049a6d407b17af50d6ae8c6386fPhil Carmody test_end();
8fcf84e6b7a55049a6d407b17af50d6ae8c6386fPhil Carmody}
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainenstatic void test_json_append_escaped(void)
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen{
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen string_t *str = t_str_new(32);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
baf3e87e186453fda13bd21f7cbcb2efc8492e8bTimo Sirainen test_begin("json_append_escaped()");
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen json_append_escaped(str, "\b\f\r\n\t\"\\\001\002-\xC3\xA4");
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0001\\u0002-\xC3\xA4") == 0);
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen test_end();
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen}
5550482ce58e51584f83c10a4c63d35b66431742Timo Sirainen
void test_json_parser(void)
{
test_json_parser_success(TRUE);
test_json_parser_success(FALSE);
test_json_append_escaped();
}