test-json-parser.c revision 86bdb644d147a73df85abce4325254d694217a5f
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen#include "test-lib.h"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen#include "str.h"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen#include "istream-private.h"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen#include "json-parser.h"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen#define TYPE_SKIP 100
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen#define TYPE_STREAM 101
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainenstatic const char json_input[] =
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "{\n"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\t\"key\"\t:\t\t\"string\","
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen " \"key2\" : 1234, \n"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key3\":true,"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key4\":false,"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"skip1\": \"jsifjaisfjiasji\","
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"skip2\": { \"x\":{ \"y\":123}, \"z\":[5,[6],{\"k\":0},3]},"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key5\":null,"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key6\": {},"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key7\": {"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen " \"sub1\":\"value\""
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "},"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key8\": {"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen " \"sub2\":-12.456,\n"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen " \"sub3\":12.456e9,\n"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen " \"sub4\":0.456e-789"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "},"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key9\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\uffff\","
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key10\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\uffff\","
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key11\": [],"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "\"key12\": [ \"foo\" , 5.24,[true],{\"aobj\":[]}]"
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen "}\n";
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainenstatic struct {
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen enum json_type type;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen const char *value;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen} json_output[] = {
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_STRING, "string" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key2" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_NUMBER, "1234" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key3" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_TRUE, "true" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key4" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_FALSE, "false" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "skip1" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { TYPE_SKIP, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "skip2" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { TYPE_SKIP, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key5" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_NULL, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key6" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_END, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key7" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT, NULL },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub1" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_STRING, "value" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_END, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key8" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "sub2" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_NUMBER, "-12.456" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub3" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_NUMBER, "12.456e9" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub4" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_NUMBER, "0.456e-789" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_END, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key9" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_STRING, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key10" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { TYPE_STREAM, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key11" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_ARRAY, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_ARRAY_END, NULL },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key12" },
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen { JSON_TYPE_ARRAY, NULL },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_STRING, "foo" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_NUMBER, "5.24" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_ARRAY, NULL },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_TRUE, "true" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_ARRAY_END, NULL },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT, NULL },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT_KEY, "aobj" },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_ARRAY, NULL },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_ARRAY_END, NULL },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_OBJECT_END, NULL },
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen { JSON_TYPE_ARRAY_END, NULL }
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen};
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainenstatic int
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainenstream_read_value(struct istream **input, const char **value_r)
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen{
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen const unsigned char *data;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen size_t size;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen ssize_t ret;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen while ((ret = i_stream_read(*input)) > 0) ;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen if (ret == 0)
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen return 0;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen i_assert(ret == -1);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen if ((*input)->stream_errno != 0)
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen return -1;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen data = i_stream_get_data(*input, &size);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen *value_r = t_strndup(data, size);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen i_stream_unref(input);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen return 1;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen}
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainenstatic void test_json_parser_success(bool full_size)
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen{
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen struct json_parser *parser;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen struct istream *input, *jsoninput = NULL;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen enum json_type type;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen const char *value, *error;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen unsigned int i, pos, json_input_len = strlen(json_input);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen int ret = 0;
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_begin(full_size ? "json parser" : "json parser (nonblocking)");
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen input = test_istream_create_data(json_input, json_input_len);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_istream_set_allow_eof(input, FALSE);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen parser = json_parser_init(input);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen i = full_size ? json_input_len : 0;
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen for (pos = 0; i <= json_input_len; i++) {
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen test_istream_set_size(input, i);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen for (;;) {
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen if (json_output[pos].type == TYPE_SKIP) {
d89f65495aa2c3922f5ede61f9fa468f89b1efd5Timo Sirainen json_parse_skip_next(parser);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen pos++;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen continue;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen } else if (json_output[pos].type != TYPE_STREAM) {
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen ret = json_parse_next(parser, &type, &value);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen } else {
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen ret = jsoninput != NULL ? 1 :
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen json_parse_next_stream(parser, &jsoninput);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen if (jsoninput != NULL)
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen ret = stream_read_value(&jsoninput, &value);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen type = TYPE_STREAM;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen }
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen if (ret <= 0)
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen break;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen i_assert(pos < N_ELEMENTS(json_output));
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_assert(json_output[pos].type == type);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_assert(null_strcmp(json_output[pos].value, value) == 0);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen pos++;
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen }
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_assert(ret == 0);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen }
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_assert(pos == N_ELEMENTS(json_output));
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_istream_set_allow_eof(input, TRUE);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_assert(json_parse_next(parser, &type, &value) == -1);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen i_stream_unref(&input);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_assert(json_parser_deinit(&parser, &error) == 0);
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen test_end();
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen}
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainenvoid test_json_parser(void)
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen{
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_json_parser_success(TRUE);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen test_json_parser_success(FALSE);
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen}
d48a2f3288eba53dd10d9d8029ec583d78a977e0Timo Sirainen