test-json-parser.c revision cd62b999690796343670d626aedf35d413c345f9
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include "test-lib.h"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include "str.h"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include "istream-private.h"
472369cba85d9f7c995dda60e7cd01d78b4a960aTimo Sirainen#include "json-parser.h"
e28fa207d1a097fa6e4a867f74ee0761472ef1ceTimo Sirainen
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen#define TYPE_SKIP 100
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen#define TYPE_STREAM 101
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen
b1f37113a5760bee842c5a7678bb5fa6f5bd8b60Timo Sirainenstatic const char json_input[] =
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen "{\n"
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen "\t\"key\"\t:\t\t\"string\","
e28fa207d1a097fa6e4a867f74ee0761472ef1ceTimo Sirainen " \"key2\" : 1234, \n"
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen "\"key3\":true,"
ef50336eefcb9ba99f73c6af37420eaf8857a39bTimo Sirainen "\"key4\":false,"
13d98ffa534f2e7d04a832c9d0153fc9c568b878Timo Sirainen "\"skip1\": \"jsifjaisfjiasji\","
13d98ffa534f2e7d04a832c9d0153fc9c568b878Timo Sirainen "\"skip2\": { \"x\":{ \"y\":123}, \"z\":[5,[6],{\"k\":0},3]},"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "\"key5\":null,"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "\"key6\": {},"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "\"key7\": {"
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen " \"sub1\":\"value\""
5694eeb99b69dea8033ca77ad69743c6b4871370Timo Sirainen "},"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "\"key8\": {"
5694eeb99b69dea8033ca77ad69743c6b4871370Timo Sirainen " \"sub2\":-12.456,\n"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen " \"sub3\":12.456e9,\n"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen " \"sub4\":0.456e-789"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "},"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "\"key9\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\uffff\","
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "\"key10\": \"foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\uffff\","
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "\"key11\": [],"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "\"key12\": [ \"foo\" , 5.24,[true],{\"aobj\":[]}]"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "}\n";
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic struct {
220e21750948941dc6e33b8f11b552fa21d7f81eTimo Sirainen enum json_type type;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen const char *value;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen} json_output[] = {
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_STRING, "string" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key2" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_NUMBER, "1234" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key3" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_TRUE, "true" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key4" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_FALSE, "false" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_OBJECT_KEY, "skip1" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { TYPE_SKIP, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "skip2" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { TYPE_SKIP, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key5" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_NULL, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key6" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_END, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key7" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub1" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_STRING, "value" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_END, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key8" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub2" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_NUMBER, "-12.456" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "sub3" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_NUMBER, "12.456e9" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_OBJECT_KEY, "sub4" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_NUMBER, "0.456e-789" },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_OBJECT_END, NULL },
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen { JSON_TYPE_OBJECT_KEY, "key9" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_STRING, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key10" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { TYPE_STREAM, "foo\\\"\b\f\n\r\t\001\xef\xbf\xbf" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key11" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_ARRAY, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_ARRAY_END, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "key12" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_ARRAY, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_STRING, "foo" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_NUMBER, "5.24" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_ARRAY, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_TRUE, "true" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_ARRAY_END, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT, NULL },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_OBJECT_KEY, "aobj" },
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen { JSON_TYPE_ARRAY, NULL },
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen { JSON_TYPE_ARRAY_END, NULL },
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen { JSON_TYPE_OBJECT_END, NULL },
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen { JSON_TYPE_ARRAY_END, NULL }
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen};
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainenstatic int
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainenstream_read_value(struct istream **input, const char **value_r)
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen{
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen const unsigned char *data;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen size_t size;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen ssize_t ret;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen while ((ret = i_stream_read(*input)) > 0) ;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (ret == 0)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return 0;
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen i_assert(ret == -1);
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen if ((*input)->stream_errno != 0)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return -1;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen data = i_stream_get_data(*input, &size);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen *value_r = t_strndup(data, size);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen i_stream_unref(input);
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen return 1;
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic void test_json_parser_success(bool full_size)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct json_parser *parser;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct istream *input, *jsoninput = NULL;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen enum json_type type;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *value, *error;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen unsigned int i, pos, json_input_len = strlen(json_input);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen int ret = 0;
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen test_begin(full_size ? "json parser" : "json parser (nonblocking)");
63e207529879438e9f4412d97cdc34bdc82a3702Timo Sirainen input = test_istream_create_data(json_input, json_input_len);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen test_istream_set_allow_eof(input, FALSE);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen parser = json_parser_init(input);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen i = full_size ? json_input_len : 0;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen for (pos = 0; i <= json_input_len; i++) {
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen test_istream_set_size(input, i);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen for (;;) {
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen if (json_output[pos].type == (enum json_type)TYPE_SKIP) {
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen json_parse_skip_next(parser);
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen pos++;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen continue;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen } else if (json_output[pos].type != (enum json_type)TYPE_STREAM) {
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen ret = json_parse_next(parser, &type, &value);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen } else {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ret = jsoninput != NULL ? 1 :
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen json_parse_next_stream(parser, &jsoninput);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (jsoninput != NULL)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ret = stream_read_value(&jsoninput, &value);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen type = TYPE_STREAM;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
3fe67ec75ccae1230bb9eb9f16affc48377f6441Timo Sirainen if (ret <= 0)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen break;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen i_assert(pos < N_ELEMENTS(json_output));
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen test_assert(json_output[pos].type == type);
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen test_assert(null_strcmp(json_output[pos].value, value) == 0);
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen pos++;
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen }
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen test_assert(ret == 0);
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen }
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen test_assert(pos == N_ELEMENTS(json_output));
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen test_istream_set_allow_eof(input, TRUE);
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen test_assert(json_parse_next(parser, &type, &value) == -1);
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen i_stream_unref(&input);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen test_assert(json_parser_deinit(&parser, &error) == 0);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen test_end();
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
5214b67a7dabab87da74e04bb8b227f94b95bce4Timo Sirainenvoid test_json_parser(void)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen test_json_parser_success(TRUE);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen test_json_parser_success(FALSE);
27586e4785d56aeb76e1fd96af8db799688dc64aTimo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen